├── .editorconfig ├── .gitignore ├── .pylintrc ├── .travis.yml ├── HISTORY.md ├── LICENSE ├── README.md ├── USAGE.md ├── aceinna.ico ├── build.spec ├── config.json ├── docs └── user manual │ ├── en.md │ └── zh.md ├── main.py ├── requirements-2.x.txt ├── requirements.txt ├── samples ├── device_detector.py └── host_in_webserver.py ├── setup.py ├── src └── aceinna │ ├── __init__.py │ ├── bootstrap │ ├── __init__.py │ ├── cli.py │ ├── default.py │ ├── loader.py │ ├── log_parser.py │ └── receiver.py │ ├── core │ ├── __init__.py │ ├── device_context.py │ ├── driver.py │ ├── event_base.py │ ├── gnss.py │ ├── packet_statistics.py │ ├── tunnel_base.py │ └── tunnel_web.py │ ├── devices │ ├── __init__.py │ ├── base │ │ ├── __init__.py │ │ ├── message_parser_base.py │ │ ├── provider_base.py │ │ ├── rtk_provider_base.py │ │ └── upgrade_worker_base.py │ ├── configs │ │ ├── __init__.py │ │ ├── openimu_predefine.py │ │ └── openrtk_predefine.py │ ├── decorator.py │ ├── device_manager.py │ ├── dmu │ │ ├── __init__.py │ │ ├── configuration_field.py │ │ ├── dmu_helper.py │ │ ├── eeprom_field.py │ │ └── uart_provider.py │ ├── ins2000 │ │ ├── __init__.py │ │ └── uart_provider.py │ ├── message_center.py │ ├── openimu │ │ ├── __init__.py │ │ └── uart_provider.py │ ├── openrtk │ │ ├── __init__.py │ │ ├── ethernet_provider.py │ │ ├── lan_provider.py │ │ └── uart_provider.py │ ├── parser_manager.py │ ├── parsers │ │ ├── __init__.py │ │ ├── dmu_field_parser.py │ │ ├── dmu_message_parser.py │ │ ├── dum_packet_parser.py │ │ ├── ins2000_message_parser.py │ │ ├── ins401_field_parser.py │ │ ├── ins401_message_parser.py │ │ ├── ins401_packet_parser.py │ │ ├── open_field_parser.py │ │ ├── open_message_parser.py │ │ └── open_packet_parser.py │ ├── ping │ │ ├── __init__.py │ │ ├── dmu.py │ │ ├── ins2000.py │ │ ├── ins401.py │ │ ├── open.py │ │ └── ping_tool.py │ ├── rtkl │ │ ├── __init__.py │ │ └── uart_provider.py │ ├── upgrade_center.py │ ├── upgrade_workers │ │ ├── __init__.py │ │ ├── ethernet_sdk_9100_worker.py │ │ ├── firmware_worker.py │ │ ├── jump_application_worker.py │ │ ├── jump_bootloader_worker.py │ │ ├── sdk_8100Bx_worker.py │ │ ├── sdk_8100_worker.py │ │ └── sdk_9100_worker.py │ └── widgets │ │ ├── __init__.py │ │ ├── ethernet_data_logger.py │ │ ├── lan_data_logger.py │ │ ├── ntrip_client.py │ │ └── odometer_listener.py │ ├── executor.py │ ├── framework │ ├── __init__.py │ ├── ans_platform_api.py │ ├── app_logger.py │ ├── command.py │ ├── communicator.py │ ├── communicators │ │ ├── __init__.py │ │ ├── ethernet_100base_t1.py │ │ ├── lan.py │ │ ├── netbios.py │ │ └── serialport.py │ ├── configuration.py │ ├── constants.py │ ├── context.py │ ├── decorator.py │ ├── file_storage.py │ ├── progress_bar.py │ ├── utils │ │ ├── __init__.py │ │ ├── dict_extend.py │ │ ├── firmware_parser.py │ │ ├── helper.py │ │ ├── print.py │ │ └── resource.py │ └── wrapper.py │ ├── libs │ ├── UserDecoderLib.dll │ ├── UserDecoderLib_linux.so │ ├── UserDecoderLib_mac.so │ └── __init__.py │ ├── models │ ├── __init__.py │ ├── args.py │ └── internal_combine_app_parse_rule.py │ ├── setting │ ├── INS2000 │ │ └── INS2000.json │ ├── INS401 │ │ └── RTK_INS │ │ │ └── ins401.json │ ├── OpenIMU300RI │ │ ├── Compass │ │ │ └── openimu.json │ │ ├── IMU │ │ │ └── openimu.json │ │ ├── INS │ │ │ └── openimu.json │ │ ├── Leveler │ │ │ └── openimu.json │ │ └── VG_AHRS │ │ │ └── openimu.json │ ├── OpenIMU300ZI │ │ ├── Compass │ │ │ └── openimu.json │ │ ├── IMU │ │ │ └── openimu.json │ │ ├── INS │ │ │ └── openimu.json │ │ ├── Leveler │ │ │ └── openimu.json │ │ └── VG_AHRS │ │ │ └── openimu.json │ ├── OpenIMU330BI │ │ ├── IMU │ │ │ └── openimu.json │ │ └── VG_AHRS │ │ │ └── openimu.json │ ├── OpenIMU335RI │ │ ├── IMU │ │ │ └── openimu.json │ │ └── VG │ │ │ └── openimu.json │ ├── OpenRTK330L │ │ └── RTK_INS │ │ │ └── openrtk.json │ ├── RTK330L │ │ └── RTK_INS │ │ │ └── RTK330L.json │ ├── __init__.py │ └── dmu │ │ └── dmu.json │ └── tools │ ├── __init__.py │ ├── can_cfg_post.py │ ├── cli.py │ ├── detector.py │ ├── openrtk_parse.py │ └── rtkl_parse.py ├── tests ├── __init__.py ├── all_cases.md ├── field_parser.py ├── firmware_parser.py ├── force_bootloader.py ├── message_center.py ├── mocker │ ├── __init__.py │ ├── communicator.py │ ├── device_access.py │ ├── devices │ │ ├── Scale1.raw │ │ ├── __init__.py │ │ ├── base.py │ │ ├── dmu.py │ │ ├── helper.py │ │ ├── openimu.py │ │ ├── openrtk.py │ │ ├── rtkl.py │ │ ├── s1.raw │ │ └── z1.raw │ └── upgrade_workers │ │ ├── error_worker.py │ │ └── normal_worker.py ├── test_ans_devices │ ├── StringStream.py │ ├── constants.py │ ├── driver_controller.py │ ├── io_manager.py │ ├── printcolor.py │ └── process_parser.py ├── test_build_message.py ├── test_communicator.py ├── test_config.py ├── test_device_manager.py ├── test_device_message_wrapper.py ├── test_device_provider.py ├── test_event_base.py ├── test_ins401_commands.py ├── test_loader.py ├── test_mag_align.py ├── test_models.py ├── test_nmea_parser.py ├── test_ntrip_client.py ├── test_parse_eth_100base_t1_buffer.py ├── test_ping.py ├── test_rtcm_parser.py ├── test_throttle.py ├── test_upgrade_center.py └── test_webserver.py └── tools ├── bootloader.py ├── ci_notify.py ├── ins2000_parser.py └── open_packet_parser.py /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.py] 4 | indent_style = space 5 | trim_trailing_whitespace = true 6 | indent_size = 4 7 | end_of_line = LF -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.log 3 | *.bin 4 | *.BIN 5 | data/*.* 6 | /.vscode 7 | /build 8 | __pycache__ 9 | dist 10 | /data 11 | *.pem 12 | *.cert 13 | *.key 14 | /setting 15 | /app_config 16 | /upgrade 17 | *.egg-info 18 | aceinna.ico 19 | .pytest_cache 20 | loggers/ 21 | /backup 22 | /libs -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | jobs: 3 | include: 4 | - name: "Python 3.8.0 on Bionic Beaver Linux" 5 | dist: bionic 6 | python: 3.8 # this works for Linux but is ignored on macOS or Windows 7 | - name: "Python 3.7.4 on macOS" 8 | os: osx 9 | osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4 10 | language: shell # 'language: python' is an error on Travis CI macOS 11 | - name: "Python 3.8.0 on Windows" 12 | os: windows # Windows 10.0.17134 N/A Build 17134 13 | language: shell # 'language: python' is an error on Travis CI Windows 14 | before_install: 15 | - choco install python --version 3.8.0 16 | - python -m pip install --upgrade pip 17 | env: PATH=/c/Python38:/c/Python38/Scripts:$PATH 18 | branches: 19 | only: 20 | - master 21 | 22 | install: 23 | - pip3 install --upgrade pip # all three OSes agree about 'pip3' 24 | - pip3 install -r requirements.txt 25 | - pip3 install pyinstaller 26 | 27 | script: 28 | - pyinstaller build.spec 29 | - if [[ ${TRAVIS_OS_NAME} == "windows" ]]; then 30 | python ./tools/ci_notify.py; 31 | else 32 | python3 ./tools/ci_notify.py; 33 | fi -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | --- 4 | ## 2.7.0 / 2023-3-31 5 | - [OpenRTK] Add CAN port parameter config in command line mode. 6 | - [OpenRTK] Add SN check logic for different version of OpenRTK (BA or BX). 7 | - [OpenRTK] Optimize the connection under lan port. 8 | - [RTK330LA] Update bootloader array of 8100 upgrade worker and 9100 upgrade worker. 9 | 10 | ## 2.6.1 / 2022-02-23 11 | - [OpenRTK] Support ST8100Bx chip upgrade. 12 | - [OpenRTK/RTK330LA] Resolve cannot ping device while upgrading. 13 | - [DMU] Resolve ping issue in baudrate 230400. 14 | - [Framework] Update native library dependency. 15 | 16 | ## 2.6.0 / 2021-10-11 17 | - [INS401] Support communication with ethernet 100base-t1 protocol. 18 | 19 | ## 2.5.0 / 2021-07-02 20 | - [DMU] Support firmware upgrade. 21 | - [OpenRTK/RTK330LA] Support ST9100 chip upgrade. 22 | - [Framework] Fix NTRIP client disconnect issue. 23 | - [Framework] Refactor upgrade worker. 24 | - [Framework] Integrate rtk log parser. 25 | - [Framework] Update startup parameters. 26 | 27 | ## 2.4.0 / 2021-05-07 28 | - [DMU] Resolve firmware version from ID message. 29 | - [DMU] Support record data in developers site. 30 | - [DMU/IMU] Support GF, SF, RF, WF for uart command run tool in developers site. 31 | - [OpenRTK/RTK330LA] Log device info when device is connected. 32 | 33 | ## 2.3.2 / 2021-04-12 34 | - [DMU] Support INS330BI 35 | - [RTK330LA] Fix the wrong GNSS port logging 36 | 37 | ## 2.3.1 / 2021-04-02 38 | - [OpenIMU] Update openimu.json for OpenIMU300RI 39 | - [OpenRTK] Update GGA parser 40 | - [OpenRTK] Split log name of RTK330LA from OpenRTK 41 | - [Framework] Optimize the device information match 42 | - [Framework] Support force to bootloader 43 | 44 | ## 2.3.0 / 2021-02-28 45 | - [OpenIMU] Support packet data statistics. 46 | - [OpenRTK] Support RTK330L. 47 | - [OpenRTK] Save predefined parameters when connected. 48 | - [Framework] Adjust the json file location. Different product would have single application json file. 49 | - [Framework] Support upgrade firmware through aceinna developers site. 50 | - [Framework] Enhance the upgrade center. 51 | - [Framework] Refactor WebServer, consider websocket server is a message tunnel. 52 | - [Framework] Refactor bootstrap, to make it clear for later maintainer. 53 | 54 | ## 2.2.4 / 2020-12-18 55 | - [OpenRTK] Remove console print and add print.log to save these infomation. 56 | - [OpenRTK] Update openrtk parse to make kml files 57 | 58 | ## 2.2.3 / 2020-12-01 59 | - [OpenRTK] Update Configuration read size 60 | - [Framework] Fix cannot parse 'sC', 'uB' command 61 | 62 | ## 2.2.2 / 2020-11-26 63 | - [OpenIMU] Add exception handler when log data, although file is closed 64 | - [OpenIMU] Add uC,uA,gA command response 65 | - [OpenRTK] Fix upgrade issue through web 66 | 67 | ## 2.2.1 / 2020-11-17 68 | 69 | - [OpenIMU] Fix the mag align command cannot correctly response. 70 | - [Framework] Update the usage of asyncio. 71 | - [Framework] Fix cannot connect the websocket server on some versions of windows. 72 | - [Framework] Support to start executor as a cli tool with startup parameter `--cli`. 73 | - [Framework] Fix data log will auto start after firmware upgrade without setting auto start. 74 | 75 | ## 2.2.0 / 2020-11-9 76 | 77 | - [OpenRTK] Important update for INS App v23.00, can't suitable for v2.0.0 or v20.00. 78 | - [OpenRTK] Modify user data packets. 79 | - [OpenRTK] Log base rtcm data on debug port. 80 | - [DMU] Add A1,A2 packet response for DUM device. 81 | - [Framework] Add Unit test cases. 82 | 83 | ## 2.1.7 / 2020-08-26 84 | 85 | - [OpenRTK] Print 'NMEA' and #INSPVA. 86 | - [Framework] Improved the ping perform on devices. 87 | 88 | ## 2.1.6 / 2020-08-19 89 | 90 | - [OpenRTK] Modify INS json: suitable for INS_APP v2.0.0. 91 | - [Framework] Improve the output of console. 92 | 93 | ## 2.1.5 / 2020-08-11 94 | 95 | - [Framework] Support download combined GNSS_RTK_SDK and INS_APP Firmware. 96 | - [Framework] Display python driver version. 97 | - [Framework] Remove upgrade file when upgrade firmware. 98 | 99 | ## 2.1.4 / 2020-07-23 100 | 101 | - [OpenRTK] Add 'rD' command to restore OpenRTK330 default configuration. 102 | User can find 'RESTORE DEFAULTS' button in OpenRTK->SETTINGS. 103 | - [OpenRTK] Add 'gB' command to get configuration according to range of parameterID. 104 | - [OpenRTK] Support update GNSS_RTK_SDK in App Center. 105 | - [Framework] Enhance the message parser from device. 106 | 107 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # Work as executable 2 | ```bash 3 | $ `./ans-device [parameters]` 4 | ``` 5 | 6 | parameters: 7 | 8 | | Name | Type | Default | Description | 9 | | - | :-: | :-: | - | 10 | | -i, --interface | String | 'default' | Value should be `uart`, `eth`, `100base-t1`. Depends on device type | 11 | | -p, --port | Number | '8000' | Value should be an available port | 12 | | --device-type | String | 'auto' | Value should be `IMU`, `RTK`, `DMU` | 13 | | -b, --baudrate | String | None | Value should be a valid baudrate. The valid value should be one of `38400`, `57600`, `115200`, `230400`, `460800` | 14 | | -c, --com-port | String | 'auto' | Value should be a COM port | 15 | | --console-log | Boolean | False | Output log on console | 16 | | --debug | Boolean | False | Log debug information | 17 | | --with-data-log | Boolean | False | Contains internal data log (OpenIMU only) | 18 | | -s, --set-user-para | Boolean | False | Set uesr parameters (OpenRTK only) | 19 | 20 | # Work as sdk 21 | Detect device 22 | ```python 23 | import time 24 | from aceinna.tools import Detector 25 | 26 | def on_find_device(device): 27 | # prepare to use 28 | device.setup(None) 29 | # get device info 30 | device.get_device_info() 31 | # start to log 32 | device.start_data_log() 33 | time.sleep(10) 34 | # stop to log 35 | device.stop_data_log() 36 | 37 | detector = Detector( 38 | device_type='IMU', 39 | com_port='COM1', 40 | baudrate=115200) 41 | detector.find(on_find_device) 42 | ``` 43 | 44 | Host in webserver 45 | ```python 46 | from aceinna.bootstrap import Default 47 | 48 | app = Default( 49 | device_type='openimu', 50 | com_port='COM1', 51 | port=8001, 52 | baudrate=115200, 53 | debug=True) 54 | app.listen() 55 | ``` 56 | 57 | 58 | # Use source code 59 | Invoke sdk and start a webserver `python main.py` -------------------------------------------------------------------------------- /aceinna.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/aceinna.ico -------------------------------------------------------------------------------- /build.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | import sys 3 | import os.path as osp 4 | sys.setrecursionlimit(5000) 5 | 6 | block_cipher = None 7 | root_path = os.path.join(os.getcwd(),'src') 8 | 9 | a = Analysis([os.path.join(root_path,'aceinna','executor.py'), 10 | ], 11 | pathex=[root_path], 12 | binaries=[], 13 | datas=[ 14 | (os.path.join(root_path,'aceinna','setting'), os.path.join('setting')), 15 | (os.path.join(root_path,'aceinna','libs'), os.path.join('libs')) 16 | ], 17 | hiddenimports=[], 18 | hookspath=[], 19 | runtime_hooks=[], 20 | excludes=[], 21 | win_no_prefer_redirects=False, 22 | win_private_assemblies=False, 23 | cipher=block_cipher, 24 | noarchive=False) 25 | pyz = PYZ(a.pure, a.zipped_data, 26 | cipher=block_cipher) 27 | exe = EXE(pyz, 28 | a.scripts, 29 | a.binaries, 30 | a.zipfiles, 31 | a.datas, 32 | [], 33 | name='ans-devices', 34 | debug=False, 35 | bootloader_ignore_signals=False, 36 | strip=False, 37 | upx=True, 38 | upx_exclude=[], 39 | runtime_tmpdir=None, 40 | console=True, 41 | icon='aceinna.ico') 42 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0" 3 | } -------------------------------------------------------------------------------- /docs/user manual/en.md: -------------------------------------------------------------------------------- 1 | # User Manual 2 | 3 | ## Contents 4 | - [Steps to use](#Steps-to-use) 5 | - [Work Mode](#Work-mode) 6 | - [Protocol](#Protocol) 7 | 8 | 9 | ## Steps to use 10 | 11 | ### 1. Run driver 12 | ```bash 13 | $ ./ans-devices 14 | ``` 15 | 16 | #### Startup Arguments 17 | You can specify some arguments while run the tool 18 | 19 | Arguments: 20 | 21 | | Name | Type | Default | Description | 22 | | - | :-: | :-: | - | 23 | | --cli | Boolean | False | Work as command line mode | 24 | | -p, --port | Number | '8000' | Value should be an available port | 25 | | --device-type | String | 'auto' | Value should be one of `IMU`, `RTK`, `DMU` | 26 | | -b, --baudrate | String | None | Value should be a valid baudrate. The valid value should be one of `38400`, `57600`, `115200`, `230400`, `460800` | 27 | | -c, --com-port | String | 'auto' | Value should be a COM port | 28 | | --console-log | Boolean | False | Output log on console | 29 | | --debug | Boolean | False | Log debug information | 30 | | --with-data-log | Boolean | False | Contains internal data log (OpenIMU only) | 31 | | -s, --set-user-para | Boolean | False | Set uesr parameters (OpenRTK only) | 32 | | -n, --ntrip-client | Boolean | False | Enable ntrip client (OpenRTK only) | 33 | | -l, --protocol | String | 'uart' | Value should be `uart`, `lan`. Depends on device type | 34 | 35 | 36 | ### 2. Connect Device 37 | Link device to your pc or mac. The tool will auto detect the linked device by default. 38 | 39 | ### 3. Data Visualization 40 | Open the Aceina developer website, different devices correspond to different visualization pages. 41 | - OpenIMU https://developers.aceinna.com/devices/record-next 42 | - OpenRTK https://developers.aceinna.com/devices/rtk 43 | 44 | ## Work Mode 45 | ### Default Mode 46 | Normally, python-openimu works as default mode. It will establish a websocket server, then exchange messages through the websocket protocol. And it should work with [aceinna developers site](https://developers.aceinna.com "Aceinna Developers Site"), it allows user to do data monitor, configuration and firmware management. 47 | 48 | ### Command Line Mode 49 | You can specify the startup argument `--cli` to switch to Command Line Mode. Command Line Mode helps you interact with device directly. And it also supply command to start a websocket server, so that you can use the full features of Default Mode. 50 | 51 | Command Line Mode supports some commands for using, below is a list of commands description, 52 | 53 | #### Help 54 | Show help menu. It would show a list of description for all supported commands. 55 | ```bash 56 | $ help 57 | ``` 58 | 59 | #### Get Device Info 60 | Show information of connected device. 61 | ```bash 62 | $ connect 63 | ``` 64 | 65 | #### Get Parameter (OpenIMU Only) 66 | Retrieve current value of specified parameter. 67 | ```bash 68 | $ get param_name 69 | ``` 70 | 71 | #### Set Parameter (OpenIMU Only) 72 | Update the value of specified parameter. The value would be recoverd after device power off. 73 | ```bash 74 | $ set param_name param_value 75 | ``` 76 | 77 | #### Save Configuration 78 | Save the configuration into EEPROM. The value would be permanently saved. 79 | ```bash 80 | $ save 81 | ``` 82 | 83 | #### Record Data (OpenIMU Only) 84 | Log the device output data in path /data. It is not supported for OpenRTK, because OpenRTK device will automatically log data when it is connected. 85 | ```bash 86 | $ record 87 | ``` 88 | 89 | #### Upgrade Firmware 90 | Upgrade firmware from a specified path. The binary file should match with the device. This is a high risk command. 91 | ```bash 92 | $ upgrade path/to/bin 93 | ``` 94 | 95 | #### Start Server 96 | Establish a websocket server. 97 | ```bash 98 | $ server_start 99 | ``` 100 | 101 | #### Stop Server 102 | Stop the websocket server. If there is websocket server runing, it has to stop it when you want to use other command. 103 | ```bash 104 | $ stop 105 | ``` 106 | 107 | #### Exit 108 | Quit from Command Line Mode 109 | ```bash 110 | $ exit 111 | ``` 112 | 113 | ## Protocol 114 | Aceinna Device could be connected with your PC via UART or LAN. The supported protocol is depended on the device type. 115 | | Device Type | Supported Protocols | Description | 116 | | - | - | - | 117 | | DMU | `uart` | | 118 | | OpenIMU | `uart` | | 119 | | OpenRTK | `uart`, `lan` | The startup argument `-l lan` is supported | 120 | | RTK330L | `uart` | | -------------------------------------------------------------------------------- /docs/user manual/zh.md: -------------------------------------------------------------------------------- 1 | # 使用手册 2 | 3 | ## 目录 4 | - [使用步骤](#使用步骤) 5 | - [工作模式](#工作模式) 6 | - [连接方式](#连接方式) 7 | 8 | ## 使用步骤 9 | 10 | ### 1. 运行driver 11 | ```bash 12 | $ ./ans-devices 13 | ``` 14 | 15 | #### 运行参数 16 | 在启动driver工具的时候,可以设置一些启动参数值, 17 | 18 | 参数列表: 19 | 20 | | 参数 | 类型 | 默认值 | 说明 | 21 | | - | :-: | :-: | - | 22 | | --cli | Boolean | False | 启动命令行模式 | 23 | | -p, --port | Number | '8000' | 设置websocket server的监听端口 | 24 | | --device-type | String | 'auto' | 指定连接设备类型,有效值是 `IMU`, `RTK`, `DMU` 之一 | 25 | | -b, --baudrate | String | None | 指定串口波特率,有效值是 `38400`, `57600`, `115200`, `230400`, `460800` 之一 | 26 | | -c, --com-port | String | 'auto' | 指定串口名称 | 27 | | --console-log | Boolean | False | 输出日志到控制台的开关 | 28 | | --debug | Boolean | False | 输出debug级别日志到控制台的开关 | 29 | | --with-data-log | Boolean | False | 自动记录设备输出数据 (OpenIMU only) | 30 | | -s, --set-user-para | Boolean | False | 使用用户设置参数的开关 (OpenRTK only) | 31 | | -n, --ntrip-client | Boolean | False | 开启Ntrip Client的开关 (OpenRTK only) | 32 | | -l, --protocol | String | 'uart' | 指定设备的连接方式。 有效值是 `uart`, `lan` 之一 | 33 | 34 | ### 2. 连接设备 35 | 将设备与电脑连接,等待一段时间,driver工具将会自动设备。也可以指定一些启动参数,可以更快地识别到设备。 36 | 37 | ### 3. 数据可视化 38 | 打开Aceinna开发者网站,不同的设备对应不同的可视化页面。 39 | - OpenIMU https://developers.aceinna.com/devices/record-next 40 | - OpenRTK https://developers.aceinna.com/devices/rtk 41 | 42 | ## 工作模式 43 | ### 默认模式 44 | 通常,driver以默认模式工作。它将建立一个websocket服务器,然后通过websocket协议交换消息。同时配合[Aceinna开发者网站](https://developers.aceinna.com“Aceinna开发者网站”),用户可以对设备进行数据监控、参数配置和固件管理。 45 | 46 | ### 命令行模式 47 | 你可以指定启动参数 `--cli` 以切换到命令行模式。命令行模式帮助您直接与设备交互。它还提供启动websocket服务器的命令,以便可以使用默认模式的全部功能。 48 | 49 | 命令行模式支持使用一些命令,下面是命令列表说明, 50 | 51 | #### 帮助 52 | 显示帮助菜单。它将显示所有受支持命令的描述列表。 53 | ```bash 54 | $ help 55 | ``` 56 | 57 | #### 获取设备信息 58 | 显示连接设备的信息。 59 | ```bash 60 | $ connect 61 | ``` 62 | 63 | #### 获取参数 (仅OpenIMU) 64 | 检索指定参数的当前值。 65 | ```bash 66 | $ get param_name 67 | ``` 68 | 69 | #### 设置参数 (仅OpenIMU) 70 | 更新指定参数的值。关闭设备电源后将恢复该值。 71 | ```bash 72 | $ set param_name param_value 73 | ``` 74 | 75 | #### 保存配置 76 | 将配置保存到EEPROM中。该值将被永久保存。 77 | ```bash 78 | $ save 79 | ``` 80 | 81 | #### 记录数据 (OpenIMU Only) 82 | 在路径 `/path` 中记录设备输出数据。OpenRTK不支持这个命令,因为OpenRTK设备在连接时会自动记录数据。 83 | ```bash 84 | $ record 85 | ``` 86 | 87 | #### 更新固件 88 | 从指定路径升级固件。固件文件应与设备匹配。这是一个高风险的操作。 89 | ```bash 90 | $ upgrade path/to/bin 91 | ``` 92 | 93 | #### 启动服务器 94 | 建立一个websocket服务器。 95 | ```bash 96 | $ server_start 97 | ``` 98 | 99 | #### 关闭服务器 100 | 停止websocket服务器。如果websocket服务器正在运行,当你想使用其他命令时,必须先停止它。 101 | ```bash 102 | $ stop 103 | ``` 104 | 105 | #### 退出 106 | 退出命令行模式 107 | ```bash 108 | $ exit 109 | ``` 110 | 111 | ## 连接方式 112 | Aceinna设备可以通过UART或LAN与PC连接。支持的协议取决于设备类型。 113 | | 设备类型 | 支持的协议 | 说明 | 114 | | - | - | - | 115 | | DMU | `uart` | | 116 | | OpenIMU | `uart` | | 117 | | OpenRTK | `uart`, `lan` | 启动时设置参数 `-l lan` 以支持网口方式连接设备 | 118 | | RTK330L | `uart` | | 119 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import runpy 4 | 5 | setattr(sys, '__dev__', True) 6 | 7 | SRC_PATH = os.path.join(os.getcwd(), 'src') 8 | EXECUTOR_PATH = os.path.join(SRC_PATH, 'aceinna', 'executor.py') 9 | 10 | sys.path.append('./src') 11 | runpy.run_path(EXECUTOR_PATH, run_name='__main__') 12 | -------------------------------------------------------------------------------- /requirements-2.x.txt: -------------------------------------------------------------------------------- 1 | azure-common==1.1.24 2 | azure-nspkg==3.0.2 3 | azure-storage-blob==2.1.0 4 | azure-storage-common==2.1.0 5 | azure-storage-nspkg==3.1.0 6 | backports-abc==0.5 7 | certifi==2019.11.28 8 | cffi==1.13.2 9 | chardet==3.0.4 10 | cryptography==3.3.2 11 | enum34==1.1.6 12 | futures==3.3.0 13 | idna==2.8 14 | ipaddress==1.0.23 15 | pathlib==1.0.1 16 | psutil==5.6.6 17 | pycparser==2.19 18 | pyserial==3.4 19 | python-dateutil==2.8.1 20 | requests==2.22.0 21 | singledispatch==3.4.0.3 22 | six==1.13.0 23 | tornado==5.1.1 24 | urllib3==1.25.7 25 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | azure-common==1.1.24 2 | azure-storage-blob==2.1.0 3 | azure-storage-common==2.1.0 4 | certifi==2019.11.28 5 | cffi==1.14.5 6 | chardet==3.0.4 7 | cryptography==3.3.2 8 | idna==2.8 9 | isodate==0.6.0 10 | msrest==0.6.10 11 | oauthlib==3.1.0 12 | psutil==5.8.0 13 | pycparser==2.19 14 | pyserial==3.4 15 | python-can==3.3.4 16 | python-dateutil==2.8.1 17 | requests==2.22.0 18 | requests-oauthlib==1.3.0 19 | six==1.13.0 20 | tornado==6.0.3 21 | urllib3==1.25.7 22 | scapy==2.4.5 23 | -------------------------------------------------------------------------------- /samples/device_detector.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | try: 4 | from aceinna.tools import Detector 5 | except: # pylint: disable=bare-except 6 | print('load package from local') 7 | sys.path.append('./src') 8 | from aceinna.tools import Detector 9 | 10 | 11 | def on_find_device(device): 12 | ''' 13 | callback after find device 14 | ''' 15 | device.setup(None) 16 | # start log 17 | device.start_data_log() 18 | 19 | print('Logging...') 20 | time.sleep(10) 21 | # stop log 22 | device.stop_data_log() 23 | device.close() 24 | 25 | 26 | def simple_start(): 27 | detector = Detector() 28 | detector.find(on_find_device) 29 | 30 | 31 | def parameters_start(): 32 | detector = Detector( 33 | device_type='IMU', 34 | com_port='COM1', 35 | baudrate=115200 36 | ) 37 | detector.find(on_find_device) 38 | 39 | if __name__ == '__main__': 40 | simple_start() 41 | # parameters_start() 42 | -------------------------------------------------------------------------------- /samples/host_in_webserver.py: -------------------------------------------------------------------------------- 1 | import sys 2 | try: 3 | from aceinna.bootstrap import Default 4 | from aceinna.framework.decorator import handle_application_exception 5 | except: # pylint: disable=bare-except 6 | print('load package from local') 7 | sys.path.append('./src') 8 | from aceinna.bootstrap import Default 9 | from aceinna.framework.decorator import handle_application_exception 10 | 11 | @handle_application_exception 12 | def simple_start(): 13 | app = Default() 14 | app.listen() 15 | 16 | @handle_application_exception 17 | def parameters_start(): 18 | app = Default( 19 | device_type='IMU', 20 | com_port='COM1', 21 | port=8001, 22 | baudrate=115200, 23 | debug=True) 24 | app.listen() 25 | 26 | 27 | if __name__ == '__main__': 28 | simple_start() 29 | # parameters_start() 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Aceinna Navigation System Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an "AS IS" # BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express # or implied. See the License for the specific language governing # permissions and limitations under the License. import json import os from setuptools import find_packages, setup from src.aceinna import (PACKAGE_NAME, VERSION) def load_json_file_path_under_setting_folder(): json_file_paths = [] setting_root_path = os.path.join(os.getcwd(), 'src', 'aceinna', 'setting') for root, dirs, files in os.walk(setting_root_path): json_file = next( (item for item in files if item.__contains__('.json')), None) if not json_file: continue json_file_path = os.path.join(root.replace( setting_root_path, 'setting'), json_file) json_file_paths.append(json_file_path) return json_file_paths def load_libraries(): file_paths = [] setting_root_path = os.path.join(os.getcwd(), 'src', 'aceinna', 'libs') for root, dirs, files in os.walk(setting_root_path): for item in files: lib_file = item if item.__contains__( '.dll') or item.__contains__('.so') else None if not lib_file: continue file_path = os.path.join(root.replace( setting_root_path, 'libs'), lib_file) file_paths.append(file_path) return file_paths def load_resources(): resources = [] json_files = load_json_file_path_under_setting_folder() lib_files = load_libraries() resources.extend(json_files) resources.extend(lib_files) return resources PACKAGE_DESCRIPTION = "Aceinna Navigation System Open Devices Library" INSTALL_REQUIRES = [ "pyserial", "pathlib", "psutil", "azure-storage-blob==2.1.0", "tornado", "python-can" ] setup( name=PACKAGE_NAME, version=VERSION, author="Aceinna, Inc", author_email="info@aceinna.com", description=PACKAGE_DESCRIPTION, long_description=open("README.md").read(), long_description_content_type="text/markdown", url="https://github.com/Aceinna/python-openimu", license="Apache 2.0", python_requires=">=2.7, !=3.0.*, !=3.1.*", install_requires=INSTALL_REQUIRES, packages=find_packages("src", exclude=['test', 'tests']), package_dir={"": "src"}, package_data={ 'aceinna': load_resources() }, classifiers=[ "Environment :: Console", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python", ], entry_points={ "console_scripts": [ "openimu = aceinna.executor:from_command_line", ] } ) -------------------------------------------------------------------------------- /src/aceinna/__init__.py: -------------------------------------------------------------------------------- 1 | # Package Version 2 | VERSION = '2.7.0' 3 | PACKAGE_NAME = 'openimu' 4 | -------------------------------------------------------------------------------- /src/aceinna/bootstrap/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import traceback 4 | 5 | from .default import Default 6 | from .cli import CommandLine 7 | from .loader import Loader -------------------------------------------------------------------------------- /src/aceinna/bootstrap/default.py: -------------------------------------------------------------------------------- 1 | """ 2 | Default App entry 3 | """ 4 | import os 5 | import sys 6 | import time 7 | import threading 8 | from ..models import WebserverArgs 9 | 10 | from ..core.driver import (Driver, DriverEvents) 11 | from ..core.device_context import DeviceContext 12 | from ..core.tunnel_web import WebServer 13 | from ..core.tunnel_base import TunnelEvents 14 | 15 | from ..framework import AppLogger 16 | from ..framework.utils import resource 17 | from ..framework.constants import APP_TYPE 18 | from ..framework.context import APP_CONTEXT 19 | 20 | 21 | class Default: 22 | '''Default entry class 23 | ''' 24 | options = None 25 | _tunnel = None 26 | _driver = None 27 | 28 | def __init__(self, **kwargs): 29 | self._build_options(**kwargs) 30 | APP_CONTEXT.mode = APP_TYPE.DEFAULT 31 | APP_CONTEXT.para_path = kwargs.get('para_path') 32 | 33 | def listen(self): 34 | ''' 35 | Prepare components, initialize the application 36 | ''' 37 | # prepare driver 38 | threading.Thread(target=self._prepare_driver).start() 39 | # prepare tunnel 40 | threading.Thread(target=self._prepare_tunnel).start() 41 | # prepage logger 42 | self._prepare_logger() 43 | 44 | def handle_discovered(self, device_provider): 45 | device_context = DeviceContext(device_provider) 46 | APP_CONTEXT.device_context = device_context 47 | 48 | self._tunnel.notify('discovered') 49 | 50 | def handle_lost(self): 51 | self._tunnel.notify('lost') 52 | 53 | def handle_upgrade_finished(self): 54 | self._tunnel.notify('continous', 'upgrade_complete', {'success': True}) 55 | 56 | def handle_upgrade_fail(self, code, message): 57 | self._tunnel.notify('continous', 'upgrade_complete', { 58 | 'success': False, 'code': code, 'message': message}) 59 | 60 | def handle_error(self, error, message): 61 | self._tunnel.notify('lost') 62 | 63 | def handle_request(self, method, converted_method, parameters): 64 | result = self._driver.execute(converted_method, parameters) 65 | self._tunnel.notify('invoke', method, result) 66 | 67 | def handle_receive_continous_data(self, packet_type, data): 68 | self._tunnel.notify('continous', packet_type, data) 69 | 70 | def _prepare_driver(self): 71 | self._driver = Driver(self.options) 72 | 73 | self._driver.on(DriverEvents.Discovered, 74 | self.handle_discovered) 75 | 76 | self._driver.on(DriverEvents.Lost, 77 | self.handle_lost) 78 | 79 | self._driver.on(DriverEvents.UpgradeFinished, 80 | self.handle_upgrade_finished) 81 | 82 | self._driver.on(DriverEvents.UpgradeFail, 83 | self.handle_upgrade_fail) 84 | 85 | self._driver.on(DriverEvents.Error, 86 | self.handle_error) 87 | 88 | self._driver.on(DriverEvents.Continous, 89 | self.handle_receive_continous_data) 90 | 91 | self._driver.detect() 92 | 93 | def _prepare_tunnel(self): 94 | import tornado.ioloop 95 | if sys.version_info[0] > 2: 96 | import asyncio 97 | asyncio.set_event_loop(asyncio.new_event_loop()) 98 | 99 | event_loop = tornado.ioloop.IOLoop.current() 100 | 101 | self._tunnel = WebServer(self.options, event_loop) 102 | self._tunnel.on(TunnelEvents.Request, self.handle_request) 103 | self._tunnel.setup() 104 | 105 | def _prepare_logger(self): 106 | ''' 107 | Set default log handler: console logger, file logger 108 | ''' 109 | executor_path = resource.get_executor_path() 110 | log_level = 'info' 111 | if self.options.debug: 112 | log_level = 'debug' 113 | 114 | console_log = self.options.console_log 115 | 116 | APP_CONTEXT.set_logger( 117 | AppLogger( 118 | filename=os.path.join(executor_path, 'loggers', 'trace.log'), 119 | gen_file=True, 120 | level=log_level, 121 | console_log=console_log 122 | )) 123 | 124 | APP_CONTEXT.set_print_logger( 125 | AppLogger( 126 | filename=os.path.join( 127 | executor_path, 'loggers', 'print_' + time.strftime('%Y%m%d_%H%M%S') + '.log'), 128 | gen_file=True, 129 | level=log_level 130 | )) 131 | 132 | def _build_options(self, **options): 133 | self.options = WebserverArgs(**options) 134 | -------------------------------------------------------------------------------- /src/aceinna/bootstrap/loader.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Application Loader 4 | """ 5 | from .receiver import Receiver as ReceiverApp 6 | from .cli import CommandLine as CommandLineApp 7 | from .default import Default as DefaultApp 8 | from .log_parser import LogParser as LogParserApp 9 | from .. import VERSION 10 | from ..framework.constants import APP_TYPE 11 | 12 | class Loader: 13 | '''Bootstrap Factory 14 | ''' 15 | @staticmethod 16 | def create(platform, options): 17 | '''Initial bootstrap instance 18 | ''' 19 | print("[Info] Python driver version: {0} ".format(VERSION)) 20 | 21 | active_app = None 22 | if platform == APP_TYPE.DEFAULT: 23 | active_app = DefaultApp(**options) 24 | 25 | if platform == APP_TYPE.CLI: 26 | active_app = CommandLineApp(**options) 27 | 28 | if platform == APP_TYPE.RECEIVER: 29 | active_app = ReceiverApp(**options) 30 | 31 | if platform == APP_TYPE.LOG_PARSER: 32 | active_app = LogParserApp(**options) 33 | 34 | if active_app is None: 35 | raise ValueError('no matched bootstrap') 36 | 37 | return active_app 38 | -------------------------------------------------------------------------------- /src/aceinna/bootstrap/log_parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from ctypes import * 4 | from ..models import LogParserArgs 5 | from ..framework.constants import APP_TYPE 6 | from ..framework.context import APP_CONTEXT 7 | from ..framework.utils import resource 8 | 9 | 10 | def prepare_lib_folder(): 11 | executor_path = resource.get_executor_path() 12 | lib_folder_name = 'libs' 13 | 14 | # copy contents of setting file under executor path 15 | lib_folder_path = os.path.join( 16 | executor_path, lib_folder_name) 17 | 18 | if not os.path.isdir(lib_folder_path): 19 | os.makedirs(lib_folder_path) 20 | 21 | platform = sys.platform 22 | 23 | if platform.startswith('win'): 24 | lib_file = 'UserDecoderLib.dll' 25 | if platform.startswith('linux'): 26 | lib_file = 'UserDecoderLib_linux.so' 27 | if platform.startswith('darwin'): 28 | lib_file = 'UserDecoderLib_mac.so' 29 | 30 | lib_path = os.path.join(lib_folder_path, lib_file) 31 | 32 | if not os.path.isfile(lib_path): 33 | lib_content = resource.get_content_from_bundle( 34 | lib_folder_name, lib_file) 35 | if lib_content is None: 36 | raise ValueError('Lib file content is empty') 37 | 38 | with open(lib_path, "wb") as code: 39 | code.write(lib_content) 40 | 41 | return lib_path 42 | 43 | 44 | def do_parse(log_type, folder_path, kml_rate, dr_parse): 45 | lib_path = prepare_lib_folder() 46 | 47 | lib = CDLL(lib_path) 48 | for root, _, file_name in os.walk(folder_path): 49 | for fname in file_name: 50 | if (fname.startswith('user') and fname.endswith('.bin')) or (fname.startswith('ins_save') and fname.endswith('.bin')): 51 | file_path = os.path.join(folder_path, fname) 52 | if log_type == 'openrtk': 53 | lib.decode_openrtk_user(bytes(file_path, encoding='utf8')) 54 | if log_type == 'rtkl': 55 | lib.decode_openrtk_inceptio( 56 | bytes(file_path, encoding='utf8')) 57 | if log_type == 'ins401': 58 | lib.decode_ins401(bytes(file_path, encoding='utf8'), bytes( 59 | dr_parse, encoding='utf8'), kml_rate) 60 | 61 | 62 | class LogParser: 63 | _options = None 64 | _tunnel = None 65 | _driver = None 66 | 67 | def __init__(self, **kwargs): 68 | self._build_options(**kwargs) 69 | APP_CONTEXT.mode = APP_TYPE.DEFAULT 70 | 71 | def listen(self): 72 | '''Start to do parse 73 | ''' 74 | self._validate_params() 75 | 76 | do_parse(self._options.log_type, 77 | self._options.path, 78 | self._options.kml_rate, 79 | self._options.powerdr) 80 | 81 | os._exit(1) 82 | 83 | def _validate_params(self): 84 | for attr_name in ['log_type', 'path', 'kml_rate', 'powerdr']: 85 | attr_value = getattr(self._options, attr_name) 86 | if not attr_value: 87 | raise ValueError( 88 | 'Parameter {0} should have a value'.format(attr_name)) 89 | 90 | def _build_options(self, **options): 91 | self._options = LogParserArgs(**options) 92 | -------------------------------------------------------------------------------- /src/aceinna/bootstrap/receiver.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import threading 3 | 4 | from ..models import WebserverArgs 5 | from ..core.driver import (Driver, DriverEvents) 6 | from ..core.device_context import DeviceContext 7 | 8 | from ..framework.constants import APP_TYPE 9 | from ..framework.context import APP_CONTEXT 10 | from ..framework.utils import helper 11 | from ..framework.decorator import throttle 12 | 13 | from ..devices.widgets import(NTRIPClient, OdometerListener, CanOptions) 14 | 15 | 16 | class Receiver: 17 | options = None 18 | _driver = None 19 | _ntrip_client = None 20 | _odometer_listener = None 21 | 22 | def __init__(self, **kwargs) -> None: 23 | self.options = None 24 | self._driver = None 25 | self._ntrip_client = None 26 | self._odometer_listener = None 27 | self._build_options(**kwargs) 28 | APP_CONTEXT.mode = APP_TYPE.RECEIVER 29 | 30 | def listen(self): 31 | ''' 32 | Prepare components, initialize the application 33 | ''' 34 | # prepare driver 35 | threading.Thread(target=self._prepare_driver).start() 36 | 37 | def _build_options(self, **kwargs): 38 | self.options = WebserverArgs(**kwargs) 39 | 40 | def _prepare_driver(self): 41 | self._driver = Driver(self.options) 42 | 43 | self._driver.on(DriverEvents.Discovered, 44 | self.handle_discovered) 45 | self._driver.on(DriverEvents.Continous, 46 | self.handle_receive_continous_data) 47 | 48 | self._driver.detect() 49 | 50 | def handle_discovered(self, device_provider): 51 | device_context = DeviceContext(device_provider) 52 | APP_CONTEXT.device_context = device_context 53 | 54 | # prepare ntrip client 55 | if not self._ntrip_client: 56 | threading.Thread(target=self._prepare_ntrip_client).start() 57 | 58 | # prepare odometer listener 59 | if not self._odometer_listener: 60 | threading.Thread(target=self._prepare_odometer_listener).start() 61 | 62 | def handle_receive_continous_data(self, packet_type, data): 63 | if packet_type == 'gga': 64 | self._ntrip_client.send(data) 65 | 66 | def _prepare_ntrip_client(self): 67 | self._ntrip_client = NTRIPClient( 68 | APP_CONTEXT.device_context._provider.properties 69 | ) 70 | self._ntrip_client.on('parsed', self._handle_data_parsed) 71 | self._ntrip_client.run() 72 | 73 | def _handle_data_parsed(self, data): 74 | if self._driver._communicator.can_write(): 75 | self._driver._communicator.write(data) 76 | 77 | def _prepare_odometer_listener(self): 78 | self._odometer_listener = OdometerListener(CanOptions('can0', 500000)) 79 | self._odometer_listener.on('data', self._handle_wheel_speed_data) 80 | 81 | @throttle(seconds=0.01) 82 | def _handle_wheel_speed_data(self, data): 83 | avg_speed = (data[2]+data[3])/2 84 | speed = avg_speed / 3600 * 1000 85 | command = helper.build_packet('cA', list( 86 | struct.unpack("4B", struct.pack(" 0: 27 | for handler in handlers: 28 | handler(*args, **kwargs) 29 | -------------------------------------------------------------------------------- /src/aceinna/core/packet_statistics.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import time 3 | 4 | 5 | def calculate_collect(packet_collection, failure_collection, key): 6 | ''' Calculate collect data as statistics 7 | ''' 8 | received = 0 9 | crc_failures = 0 10 | rate = 0 11 | 12 | if key in packet_collection: 13 | received = packet_collection[key]['received'] 14 | rate = packet_collection[key]['rate'] 15 | 16 | if key in failure_collection: 17 | crc_failures = failure_collection[key] 18 | 19 | calculate_result = { 20 | 'received': received, 21 | 'failures': crc_failures, 22 | 'rate': rate, 23 | # 'event_time': event_time 24 | } 25 | 26 | return calculate_result 27 | 28 | 29 | class PacketStatistics: 30 | ''' Packet Statistics Service 31 | ''' 32 | # { 33 | # 'z1': Freeze sized Queue, 34 | # 'pos': Freeze sized Queue, 35 | # } 36 | _packet_collect_dict = {} 37 | _failure_collect_dict = {} 38 | _last_statistics = None 39 | _last_time = None 40 | 41 | def _get_packet_types(self): 42 | packet_types_in_success = self._packet_collect_dict.keys() 43 | packet_types_in_failure = self._failure_collect_dict.keys() 44 | packet_types = list(packet_types_in_success) 45 | 46 | for packet_type in packet_types_in_failure: 47 | if packet_type not in packet_types: 48 | packet_types.append(packet_type) 49 | 50 | if len(packet_types) == 0: 51 | return None 52 | 53 | return packet_types 54 | 55 | def collect(self, collect_type, packet_type, event_time): 56 | ''' Collect packet type 57 | ''' 58 | if collect_type == 'success': 59 | if packet_type not in self._packet_collect_dict: 60 | self._packet_collect_dict[packet_type] = { 61 | 'received': 0, 62 | # max calculate 500Hz 63 | 'sampling': collections.deque(maxlen=500), 64 | 'rate': 0, 65 | 'last_calculate_time': event_time, 66 | } 67 | 68 | self._packet_collect_dict[packet_type]['received'] += 1 69 | self._packet_collect_dict[packet_type]['sampling'].append( 70 | event_time 71 | ) 72 | 73 | last_calculate_time = self._packet_collect_dict[packet_type]['last_calculate_time'] 74 | duration = event_time - last_calculate_time 75 | 76 | if duration >= 1: 77 | count = 0 78 | 79 | try: 80 | start = self._packet_collect_dict[packet_type]['sampling'].index( 81 | last_calculate_time) 82 | end = self._packet_collect_dict[packet_type]['sampling'].index( 83 | event_time) 84 | count = end - start 85 | except ValueError: 86 | count = 0 87 | 88 | self._packet_collect_dict[packet_type]['rate'] = round( 89 | count/duration, 1) 90 | 91 | self._packet_collect_dict[packet_type]['last_calculate_time'] = event_time 92 | 93 | if collect_type == 'fail': 94 | if packet_type not in self._failure_collect_dict: 95 | self._failure_collect_dict[packet_type] = 0 96 | 97 | self._failure_collect_dict[packet_type] += 1 98 | 99 | def reset(self): 100 | ''' Reset statistics 101 | ''' 102 | for packet_type in self._packet_collect_dict: 103 | self._packet_collect_dict[packet_type]['received'] = 0 104 | self._packet_collect_dict[packet_type]['rate'] = 0 105 | 106 | for packet_type in self._failure_collect_dict: 107 | self._failure_collect_dict[packet_type] = 0 108 | 109 | self._last_time = None 110 | 111 | def get_result(self): 112 | ''' Get statistics result 113 | ''' 114 | packet_types = self._get_packet_types() 115 | 116 | if not packet_types: 117 | return None 118 | 119 | result = {} 120 | for key in packet_types: 121 | statistics_result = calculate_collect( 122 | self._packet_collect_dict, 123 | self._failure_collect_dict, 124 | key 125 | ) 126 | result[key] = statistics_result 127 | 128 | # diff the last statistics, if no change, return None 129 | if self._last_statistics == result: 130 | return None 131 | 132 | self._last_statistics = result 133 | 134 | return result 135 | -------------------------------------------------------------------------------- /src/aceinna/core/tunnel_base.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | from .event_base import EventBase 3 | 4 | 5 | class TunnelEvents: 6 | '''Tunnel Events defination 7 | ''' 8 | Request = 'REQUEST' 9 | 10 | 11 | class TunnelBase(EventBase): 12 | ''' Tunnel Base 13 | ''' 14 | __metaclass__ = ABCMeta 15 | 16 | @abstractmethod 17 | def setup(self): 18 | ''' Prepare the tunnel 19 | ''' 20 | 21 | @abstractmethod 22 | def notify(self, notify_type, *other): 23 | ''' Notify to clients 24 | ''' 25 | -------------------------------------------------------------------------------- /src/aceinna/devices/__init__.py: -------------------------------------------------------------------------------- 1 | from .device_manager import DeviceManager 2 | -------------------------------------------------------------------------------- /src/aceinna/devices/base/__init__.py: -------------------------------------------------------------------------------- 1 | from ...core.event_base import EventBase 2 | from .provider_base import OpenDeviceBase 3 | from .upgrade_worker_base import UpgradeWorkerBase 4 | -------------------------------------------------------------------------------- /src/aceinna/devices/base/message_parser_base.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | from . import EventBase 3 | from ...framework.utils import helper 4 | 5 | class MessageParserBase(EventBase): 6 | ''' 7 | Message parser base 8 | ''' 9 | __metaclass__ = ABCMeta 10 | 11 | properties = None 12 | run_command = '' 13 | 14 | def __init__(self, configuration): 15 | super(MessageParserBase, self).__init__() 16 | self.properties = configuration 17 | 18 | @abstractmethod 19 | def set_run_command(self, command): 20 | ''' 21 | set current run command 22 | ''' 23 | 24 | @abstractmethod 25 | def analyse(self, data_block): 26 | ''' 27 | Analyse the data per byte 28 | ''' 29 | 30 | def set_configuration(self, configuration): 31 | ''' 32 | load configuration 33 | ''' 34 | self.properties = configuration 35 | 36 | def get_packet_info(self, raw_command): 37 | ''' 38 | Build packet info 39 | ''' 40 | packet_type, payload, _ = helper.parse_command_packet(raw_command) 41 | return { 42 | 'packet_type': packet_type, 43 | 'data': payload, 44 | 'raw': raw_command 45 | } 46 | -------------------------------------------------------------------------------- /src/aceinna/devices/base/upgrade_worker_base.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | from . import EventBase 3 | 4 | 5 | class UpgradeWorkerBase(EventBase): 6 | ''' 7 | Upgrade worker base 8 | ''' 9 | __metaclass__ = ABCMeta 10 | 11 | def __init__(self): 12 | super(UpgradeWorkerBase, self).__init__() 13 | self._name = None 14 | self._key = None 15 | self._group = None 16 | self._is_stopped = False 17 | 18 | @property 19 | def name(self): 20 | return self._name 21 | 22 | @name.setter 23 | def name(self, value): 24 | self._name = value 25 | 26 | @property 27 | def key(self): 28 | return self._key 29 | 30 | @key.setter 31 | def key(self, value): 32 | self._key = value 33 | 34 | @property 35 | def group(self): 36 | return self._group 37 | 38 | @group.setter 39 | def group(self, value): 40 | self._group = value 41 | 42 | @property 43 | def is_stopped(self): 44 | return self._is_stopped 45 | 46 | @abstractmethod 47 | def get_upgrade_content_size(self): 48 | '''get the size of upgrade content''' 49 | return 0 50 | 51 | @abstractmethod 52 | def work(self): 53 | '''do the work''' 54 | 55 | @abstractmethod 56 | def stop(self): 57 | '''stop work''' 58 | -------------------------------------------------------------------------------- /src/aceinna/devices/configs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/devices/configs/__init__.py -------------------------------------------------------------------------------- /src/aceinna/devices/configs/openimu_predefine.py: -------------------------------------------------------------------------------- 1 | """ 2 | predefined params for openimu 3 | """ 4 | JSON_FILE_NAME = 'openimu.json' 5 | 6 | DEFAULT_PRODUCT_NAME = 'OpenIMU300ZI' 7 | 8 | def get_openimu_products(): 9 | return { 10 | 'OpenIMU300RI': ['Compass', 'IMU', 'INS', 'Leveler', 'VG_AHRS'], 11 | 'OpenIMU300ZI': ['Compass', 'IMU', 'INS', 'Leveler', 'VG_AHRS'], 12 | 'OpenIMU330BI': ['IMU', 'VG_AHRS'], 13 | 'OpenIMU335RI': ['IMU', 'VG'] 14 | } 15 | 16 | 17 | def get_app_names(): 18 | ''' 19 | define openimu app type 20 | ''' 21 | app_names = ['Compass', 22 | 'IMU', 23 | 'INS', 24 | 'Leveler', 25 | 'OpenIMU', 26 | 'VG', 27 | 'VG_AHRS', 28 | ] 29 | return app_names 30 | 31 | 32 | APP_STR = ['INS', 'VG', 'VG_AHRS', 'Compass', 'Leveler', 'IMU'] 33 | -------------------------------------------------------------------------------- /src/aceinna/devices/configs/openrtk_predefine.py: -------------------------------------------------------------------------------- 1 | """ 2 | predefined params for openrtk 3 | """ 4 | 5 | 6 | def get_openrtk_products(): 7 | return { 8 | 'OpenRTK330L': ['RTK_INS'], 9 | 'RTK330L': ['RTK_INS'], 10 | 'INS401':['RTK_INS'] 11 | } 12 | 13 | 14 | def get_app_names(): 15 | ''' 16 | define openimu app type 17 | ''' 18 | app_names = ['RTK_INS'] 19 | return app_names 20 | 21 | 22 | def get_configuratin_file_mapping(): 23 | return { 24 | 'OpenRTK330L': 'openrtk.json', 25 | 'RTK330L': 'RTK330L.json', 26 | 'INS401':'ins401.json' 27 | } 28 | 29 | 30 | APP_STR = ['RTK_INS'] 31 | -------------------------------------------------------------------------------- /src/aceinna/devices/decorator.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from .message_center import (DeviceMessage) 3 | 4 | 5 | def with_device_message(func): 6 | ''' 7 | This is a decorator for method with DeviceMessage, it would looks like 8 | code: yield message_center.build(command=command_line) 9 | ''' 10 | 11 | @functools.wraps(func) 12 | def wrapper(*args, **kwargs): 13 | generator_func = func(*args, **kwargs) 14 | global generator_result 15 | generator_result = None 16 | 17 | def check_result(): 18 | global generator_result 19 | while not generator_result: 20 | continue 21 | 22 | if generator_result: 23 | next_device_message = generator_func.send(generator_result) 24 | if isinstance(next_device_message, DeviceMessage): 25 | generator_result = None 26 | next_device_message.on('finished', on_resolve) 27 | next_device_message.send() 28 | return check_result() 29 | else: 30 | return next_device_message 31 | 32 | def on_resolve(*args, **kwargs): 33 | global generator_result 34 | generator_result = { 35 | 'packet_type': kwargs['packet_type'], 36 | 'data': kwargs['data'], 37 | 'error': kwargs['error'], 38 | 'raw': kwargs['raw'] 39 | } 40 | 41 | try: 42 | device_message = generator_func.send(None) 43 | if isinstance(device_message, DeviceMessage): 44 | device_message.on('finished', on_resolve) 45 | device_message.send() 46 | return check_result() 47 | else: 48 | return device_message 49 | except StopIteration as ex: 50 | value = { 51 | 'packetType': 'error', 52 | 'data': 'No Response' 53 | } 54 | 55 | if hasattr(ex, 'value'): 56 | value = ex.value 57 | return value 58 | 59 | return wrapper 60 | -------------------------------------------------------------------------------- /src/aceinna/devices/dmu/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/devices/dmu/__init__.py -------------------------------------------------------------------------------- /src/aceinna/devices/dmu/configuration_field.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from ..parsers.dmu_field_parser import (encode_value, decode_value) 3 | from ...framework.utils.print import print_yellow 4 | 5 | class ConfigurationField(object): 6 | ''' 7 | Configuration Field 8 | ''' 9 | 10 | def __init__(self, name, field_id, field_type): 11 | self.name = name 12 | self.field_id = field_id 13 | self.field_type = field_type 14 | 15 | def parse(self, payload): 16 | ''' 17 | Parse payload 18 | ''' 19 | # return: value, parsed, error 20 | value = decode_value(self.field_type, payload) 21 | return value, True, None 22 | 23 | def encode(self, value): 24 | return encode_value(self.field_type, value) 25 | 26 | 27 | class ConfigruationFieldDefines: 28 | ''' 29 | A list of fields could be parsed 30 | ''' 31 | 32 | def __init__(self): 33 | self._list = dict() 34 | 35 | def load(self, json): 36 | self._list.clear() 37 | for item in json: 38 | param_id = item['paramId'] 39 | param_name = item['name'] 40 | param_type = item['type'] 41 | self._list[param_id] = ConfigurationField( 42 | param_name, param_id, param_type) 43 | 44 | def get_fields(self) -> List[ConfigurationField]: 45 | return self._list.values() 46 | 47 | def find(self, param_id: int) -> ConfigurationField: 48 | exist_field = self._list.get(param_id) 49 | if exist_field: 50 | return exist_field 51 | 52 | print_yellow('Cannot find field {0}, because the field is not defined in configuration file'.format(param_id)) 53 | return ConfigurationField('Unknown', param_id, 'Unknown') 54 | 55 | 56 | CONFIGURATION_FIELD_DEFINES_SINGLETON = ConfigruationFieldDefines() 57 | -------------------------------------------------------------------------------- /src/aceinna/devices/dmu/eeprom_field.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from abc import ABCMeta, abstractmethod 3 | 4 | 5 | def bytes2binstr(b, n=None): 6 | s = ''.join(f'{x:08b}' for x in b) 7 | return s if n is None else s[:n + n // 8 + (0 if n % 8 else -1)] 8 | 9 | 10 | def getbytes(bits): 11 | done = False 12 | while not done: 13 | byte = 0 14 | for _ in range(0, 8): 15 | try: 16 | bit = next(bits) 17 | except StopIteration: 18 | bit = 0 19 | done = True 20 | byte = (byte << 1) | bit 21 | yield byte 22 | 23 | 24 | class EEPROMField(object): 25 | ''' 26 | EEPROM Field 27 | ''' 28 | 29 | def __init__(self, name, address, word_len=1): 30 | self.name = name 31 | self.address = address 32 | self.word_len = word_len 33 | 34 | @abstractmethod 35 | def parse(self, payload): 36 | ''' 37 | Parse payload 38 | ''' 39 | # return: value, parsed, error 40 | return payload, False, None 41 | 42 | 43 | class ProductConfigurationField(EEPROMField): 44 | def __init__(self, name, address, word_len=1): 45 | super(ProductConfigurationField, self).__init__( 46 | name, address, word_len=1) 47 | 48 | def parse(self, payload): 49 | # return: value, parsed, error 50 | bytes_value = struct.pack('BB', *payload) 51 | bit_value = bytes2binstr(bytes_value) 52 | 53 | parsed_value = { 54 | 'mags': int(bit_value[-1]), 55 | 'gps': int(bit_value[-2]), 56 | 'algorithm': int(bit_value[-3]), 57 | 'ext_aiding': int(bit_value[-4]), 58 | 'architechture': int(bit_value[-8:-4], 2) 59 | } 60 | 61 | return parsed_value, True, None 62 | 63 | 64 | EEPROM_ADDRESS_DEFINES = [ 65 | { 66 | "address": 0x71C, 67 | "instance": ProductConfigurationField('Product Configuration', 0x71C) 68 | } 69 | ] 70 | 71 | 72 | class EEPROMFieldDefines: 73 | ''' 74 | A list of fields could be parsed 75 | ''' 76 | 77 | def __init__(self): 78 | self._list = dict() 79 | self._default_field = EEPROMField('default', -1) 80 | 81 | def load(self): 82 | self._list.clear() 83 | for item in EEPROM_ADDRESS_DEFINES: 84 | self._list[item['address']] = item['instance'] 85 | 86 | def find(self, address: int) -> EEPROMField: 87 | exist_field = self._list.get(address) 88 | 89 | if not exist_field: 90 | exist_field = self._default_field 91 | 92 | return exist_field 93 | 94 | 95 | EEPROM_FIELD_DEFINES_SINGLETON = EEPROMFieldDefines() 96 | -------------------------------------------------------------------------------- /src/aceinna/devices/ins2000/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/devices/ins2000/__init__.py -------------------------------------------------------------------------------- /src/aceinna/devices/openimu/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/devices/openimu/__init__.py -------------------------------------------------------------------------------- /src/aceinna/devices/openrtk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/devices/openrtk/__init__.py -------------------------------------------------------------------------------- /src/aceinna/devices/openrtk/uart_provider.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import serial 4 | import serial.tools.list_ports 5 | import struct 6 | 7 | from ...framework.context import APP_CONTEXT 8 | from ..base.rtk_provider_base import RTKProviderBase 9 | 10 | from ..upgrade_workers import ( 11 | FirmwareUpgradeWorker, 12 | UPGRADE_EVENT, 13 | SDK8100UpgradeWorker, 14 | SDK8100BxUpgradeWorker 15 | ) 16 | from ...framework.utils import helper 17 | from ...framework.utils.print import print_red 18 | 19 | 20 | class Provider(RTKProviderBase): 21 | ''' 22 | OpenRTK UART provider 23 | ''' 24 | 25 | def __init__(self, communicator, *args): 26 | super(Provider, self).__init__(communicator) 27 | self.bootloader_baudrate = 115200 28 | self.config_file_name = 'openrtk.json' 29 | self.device_category = 'OpenRTK' 30 | self.port_index_define = { 31 | 'user': 0, 32 | 'rtcm': 1, 33 | 'debug': 2, 34 | } 35 | 36 | def thread_debug_port_receiver(self, *args, **kwargs): 37 | if self.debug_logf is None: 38 | return 39 | 40 | cmd_log = 'log debug on\r\n' 41 | self.debug_serial_port.write(cmd_log.encode()) 42 | 43 | # log data 44 | while True: 45 | try: 46 | data = bytearray(self.debug_serial_port.read_all()) 47 | except Exception as e: 48 | print_red('DEBUG PORT Thread error: {0}'.format(e)) 49 | return # exit thread receiver 50 | if data and len(data) > 0: 51 | self.debug_logf.write(data) 52 | else: 53 | time.sleep(0.001) 54 | 55 | def thread_rtcm_port_receiver(self, *args, **kwargs): 56 | if self.rtcm_logf is None: 57 | return 58 | while True: 59 | try: 60 | data = bytearray(self.rtcm_serial_port.read_all()) 61 | except Exception as e: 62 | print_red('RTCM PORT Thread error: {0}'.format(e)) 63 | return # exit thread receiver 64 | if len(data): 65 | self.rtcm_logf.write(data) 66 | else: 67 | time.sleep(0.001) 68 | 69 | def before_write_content(self): 70 | self.communicator.serial_port.baudrate = self.bootloader_baudrate 71 | self.communicator.serial_port.reset_input_buffer() 72 | 73 | def firmware_write_command_generator(self, data_len, current, data): 74 | command_WA = 'WA' 75 | message_bytes = [] 76 | message_bytes.extend(struct.pack('>I', current)) 77 | message_bytes.extend(struct.pack('B', data_len)) 78 | message_bytes.extend(data) 79 | return helper.build_packet(command_WA, message_bytes) 80 | 81 | # override 82 | 83 | def build_worker(self, rule, content): 84 | device_info = self.get_device_connection_info() 85 | openrtk_sn = int(device_info['serialNumber']) 86 | if rule == 'rtk': 87 | firmware_worker = FirmwareUpgradeWorker( 88 | self.communicator, content, 89 | self.firmware_write_command_generator) 90 | firmware_worker.on(UPGRADE_EVENT.BEFORE_WRITE, 91 | lambda: self.before_write_content()) 92 | firmware_worker.on( 93 | UPGRADE_EVENT.FIRST_PACKET, lambda: time.sleep(8)) 94 | return firmware_worker 95 | 96 | if rule == 'sdk': 97 | sdk_port = '' 98 | if (self.properties["initial"]["useDefaultUart"]): 99 | user_port_num, port_name = self.build_connected_serial_port_info() 100 | sdk_port = port_name + str(int(user_port_num) + 3) 101 | else: 102 | for uart in self.properties["initial"]["uart"]: 103 | if uart['enable'] == 1: 104 | if uart['name'] == 'SDK': 105 | sdk_port = uart["value"] 106 | 107 | sdk_uart = serial.Serial( 108 | sdk_port, self.bootloader_baudrate, timeout=0.1) 109 | if not sdk_uart.isOpen(): 110 | raise Exception('Cannot open SDK upgrade port') 111 | BA_sn = list(range(2275000161, 2275000170+1)) + list(range(2275000181, 2275000191+1)) + list(range(2275000193, 2275000220+1)) 112 | if (openrtk_sn in BA_sn) or (openrtk_sn < 2275000000): 113 | return SDK8100UpgradeWorker(sdk_uart, content) 114 | else: 115 | return SDK8100BxUpgradeWorker(sdk_uart, content) 116 | 117 | # command list 118 | # use base methods 119 | -------------------------------------------------------------------------------- /src/aceinna/devices/parser_manager.py: -------------------------------------------------------------------------------- 1 | from .parsers.open_message_parser import UartMessageParser as OpenUartMessageParser 2 | from .parsers.dmu_message_parser import UartMessageParser as DMUUartMessageParser 3 | from .parsers.ins2000_message_parser import UartMessageParser as INS2000UartMessageParser 4 | from .parsers.ins401_message_parser import EthernetMessageParser as INS401EthernetMessageParser 5 | 6 | class ParserManager: 7 | ''' 8 | Manage Parser 9 | ''' 10 | device_list = [] 11 | 12 | # TODO: communicator_type should be used to generate the parser 13 | @staticmethod 14 | def build(device_type, communicator_type, properties): # pylint:disable=unused-argument 15 | ''' 16 | Generate matched parser 17 | ''' 18 | if device_type == 'DMU': 19 | return DMUUartMessageParser(properties) 20 | elif device_type == 'INS2000': 21 | return INS2000UartMessageParser(properties) 22 | elif device_type == 'INS401': 23 | return INS401EthernetMessageParser(properties) 24 | else: 25 | return OpenUartMessageParser(properties) 26 | -------------------------------------------------------------------------------- /src/aceinna/devices/parsers/__init__.py: -------------------------------------------------------------------------------- 1 | import math 2 | def filter_nan(value): 3 | ''' 4 | Filter NaN 5 | ''' 6 | if not isinstance(value, float): 7 | return value 8 | 9 | if math.isnan(value): 10 | return 0 11 | else: 12 | return value -------------------------------------------------------------------------------- /src/aceinna/devices/parsers/ins401_message_parser.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import operator 3 | import time 4 | import struct 5 | from ..base.message_parser_base import MessageParserBase 6 | from ...framework.utils import helper 7 | from ...framework.context import APP_CONTEXT 8 | from .ins401_packet_parser import ( 9 | match_command_handler, common_continuous_parser, other_output_parser) 10 | 11 | MSG_HEADER = [0x55, 0x55] 12 | PACKET_TYPE_INDEX = 2 13 | PAYLOAD_LEN_INDEX = 8 14 | INPUT_PACKETS = [ 15 | b'\x01\xcc', # Get device information 16 | b'\x02\xcc', # Get parameter 17 | b'\x03\xcc', # Set parameter 18 | b'\x04\xcc', # Save config 19 | b'\x01\x0b', # Send odometer 20 | b'\x02\x0b' # Send NMEA 21 | ] 22 | OUTPUT_PACKETS = [ 23 | b'\x01\n', # IMU 100hz 30 3000 24 | b'\x02\n', # GNSS 1hz 77 77 25 | b'\x03\n', # INS 100hz 108 10800 26 | b'\x04\n', # Odometer 20hz 500 27 | b'\x05\n', # diagnostic 1hz 31 31 28 | b'\x06\n' # RTCM Rover 3000 29 | ] 30 | 31 | 32 | class EthernetMessageParser(MessageParserBase): 33 | def __init__(self, configuration): 34 | super(EthernetMessageParser, self).__init__(configuration) 35 | 36 | def set_run_command(self, command): 37 | pass 38 | 39 | def analyse(self, data): 40 | sync_pattern = data[0:2] 41 | if operator.eq(list(sync_pattern), MSG_HEADER) and len(data) >= PAYLOAD_LEN_INDEX: 42 | payload_len_byte = bytes(data[4:PAYLOAD_LEN_INDEX]) 43 | payload_len = struct.unpack('H', packet_type_byte)[0] 47 | 48 | if len(data) < PAYLOAD_LEN_INDEX + payload_len + 2: 49 | APP_CONTEXT.get_logger().logger.info( 50 | "crc check error! packet_type:{0}".format(packet_type)) 51 | 52 | self.emit('crc_failure', packet_type=packet_type, 53 | event_time=time.time()) 54 | print('crc_failure', packet_type=packet_type, 55 | event_time=time.time()) 56 | return 57 | 58 | result = helper.calc_crc(data[2:PAYLOAD_LEN_INDEX+payload_len]) 59 | 60 | if result[0] == data[PAYLOAD_LEN_INDEX + payload_len] and result[1] == data[PAYLOAD_LEN_INDEX + payload_len + 1]: 61 | self._parse_message(packet_type_byte, payload_len, data) 62 | else: 63 | APP_CONTEXT.get_logger().logger.info( 64 | "crc check error! packet_type:{0}".format(packet_type)) 65 | 66 | self.emit('crc_failure', packet_type=packet_type, 67 | event_time=time.time()) 68 | input_packet_config = next( 69 | (x for x in self.properties['userMessages']['inputPackets'] 70 | if x['name'] == packet_type), None) 71 | if input_packet_config: 72 | self.emit('command', 73 | packet_type=packet_type, 74 | data=[], 75 | error=True, 76 | raw=data) 77 | 78 | def _parse_message(self, packet_type, payload_len, frame): 79 | payload = frame[PAYLOAD_LEN_INDEX:payload_len+PAYLOAD_LEN_INDEX] 80 | # parse interactive commands 81 | is_interactive_cmd = INPUT_PACKETS.__contains__(packet_type) 82 | 83 | if is_interactive_cmd: 84 | self._parse_input_packet(packet_type, payload, frame) 85 | else: 86 | # consider as output packet, parse output Messages 87 | self._parse_output_packet(packet_type, payload, frame) 88 | 89 | def _parse_input_packet(self, packet_type, payload, frame): 90 | payload_parser = match_command_handler(packet_type) 91 | 92 | if payload_parser: 93 | data, error = payload_parser( 94 | payload, self.properties['userConfiguration']) 95 | self.emit('command', 96 | packet_type=packet_type, 97 | data=data, 98 | error=error, 99 | raw=frame) 100 | else: 101 | print('[Warning] Unsupported command {0}'.format( 102 | packet_type.encode())) 103 | 104 | def _parse_output_packet(self, packet_type, payload, frame): 105 | # check if it is the valid out packet 106 | is_output_packet = OUTPUT_PACKETS.__contains__(packet_type) 107 | if is_output_packet: 108 | 109 | self.emit('continuous_message', 110 | packet_type=packet_type, 111 | data=payload, 112 | event_time=time.time(), 113 | raw=frame) 114 | return 115 | -------------------------------------------------------------------------------- /src/aceinna/devices/ping/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/devices/ping/__init__.py -------------------------------------------------------------------------------- /src/aceinna/devices/ping/dmu.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | import time 4 | from ..dmu import dmu_helper 5 | from ...framework.utils import (helper, resource) 6 | from ...framework.context import APP_CONTEXT 7 | 8 | ID = [0x49, 0x44] 9 | VR = [0x56, 0x52] 10 | 11 | 12 | def _run_command(communicator, message_type, response_message_type, message_bytes=[], size=1000, retry=10): 13 | command_line = dmu_helper.build_packet(message_type, message_bytes) 14 | communicator.write(command_line) 15 | time.sleep(0.1) 16 | 17 | data_buffer = helper.read_untils_have_data( 18 | communicator, response_message_type, size, retry) 19 | 20 | return data_buffer 21 | 22 | 23 | def _format_string(data_buffer): 24 | parsed = bytearray(data_buffer) if data_buffer and len( 25 | data_buffer) > 0 else None 26 | 27 | formatted = '' 28 | if parsed is not None: 29 | try: 30 | if sys.version_info < (3, 0): 31 | formatted = str(struct.pack( 32 | '{0}B'.format(len(parsed)), *parsed)) 33 | else: 34 | formatted = str(struct.pack( 35 | '{0}B'.format(len(parsed)), *parsed), 'utf-8') 36 | except UnicodeDecodeError: 37 | formatted = '' 38 | 39 | return formatted 40 | 41 | 42 | def _need_check(limit_type, device_type): 43 | if limit_type is None: 44 | return True 45 | 46 | return limit_type == device_type 47 | 48 | 49 | def ping(communicator, *args): 50 | '''DMU Ping 51 | ''' 52 | filter_device_type = args[0] 53 | is_need_check = _need_check(filter_device_type, 'DMU') 54 | 55 | if not is_need_check: 56 | return None 57 | 58 | pk_result = _run_command(communicator, 'PK', 'PK') 59 | if pk_result == []: 60 | id_packet_data = _run_command(communicator, 'GP', 'ID', ID) 61 | vr_packet_data = _run_command(communicator, 'GP', 'VR', VR) 62 | if id_packet_data is None or vr_packet_data is None: 63 | return None 64 | 65 | mode_string_len = len(id_packet_data[4:]) 66 | model_string = struct.pack('{0}B'.format( 67 | mode_string_len), *id_packet_data[4:]).decode() 68 | 69 | ping_info = { 70 | 'device_type': 'DMU', 71 | 'device_info': id_packet_data, 72 | 'app_info': vr_packet_data 73 | } 74 | 75 | # return ping info if specified the device type 76 | if filter_device_type == 'DMU': 77 | return ping_info 78 | 79 | if model_string.find('OpenIMU') > -1 or \ 80 | model_string.find('OpenRTK') > -1: 81 | return None 82 | 83 | return ping_info 84 | 85 | return None 86 | -------------------------------------------------------------------------------- /src/aceinna/devices/ping/ins2000.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import struct 4 | from ...framework.utils import (helper, resource) 5 | from ...framework.context import APP_CONTEXT 6 | 7 | 8 | unlog_cmd = 'unlogall\r' 9 | version_cmd = 'version\r' 10 | 11 | def _run_command(communicator, command, size=1000, retry=10): 12 | communicator.write(command.encode()) 13 | time.sleep(0.1) 14 | 15 | read_data = communicator.read(size) 16 | out_str = "" 17 | if read_data is not None: 18 | data_buffer = bytearray(read_data) 19 | if data_buffer and len(data_buffer) > 0: 20 | try: 21 | out_str = data_buffer.decode() 22 | except Exception: 23 | return "" 24 | 25 | return out_str 26 | 27 | def ping(communicator, *args): 28 | '''INS2000 Device Ping 29 | ''' 30 | unlog_text = _run_command(communicator, unlog_cmd) 31 | if unlog_text != None and unlog_text != '': 32 | device_info_text = _run_command(communicator, version_cmd) 33 | if device_info_text.find('INS2000') > -1: 34 | device_info_sp = device_info_text.split('\r\n') 35 | return { 36 | 'device_type': 'INS2000', 37 | 'device_info': device_info_sp[1], 38 | 'app_info': device_info_sp[1] 39 | } 40 | 41 | return None 42 | -------------------------------------------------------------------------------- /src/aceinna/devices/ping/ins401.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import struct 4 | from ...framework.utils import (helper, resource) 5 | from ...framework.context import APP_CONTEXT 6 | pG = [0x01, 0xcc] 7 | 8 | 9 | def _run_command(communicator, command): 10 | run_command = helper.build_ethernet_packet(communicator.get_dst_mac(), 11 | communicator.get_src_mac(), command) 12 | communicator.reset_buffer() 13 | communicator.write(run_command.actual_command) 14 | 15 | time.sleep(0.1) 16 | data_buffer = helper.read_untils_have_data( 17 | communicator, command, retry_times=100) 18 | return data_buffer 19 | 20 | 21 | def _format_string(data_buffer): 22 | parsed = bytearray( 23 | data_buffer) if data_buffer and len(data_buffer) > 0 else None 24 | 25 | formatted = '' 26 | if parsed is not None: 27 | try: 28 | if sys.version_info < (3, 0): 29 | formatted = str( 30 | struct.pack('{0}B'.format(len(parsed)), *parsed)) 31 | else: 32 | formatted = str( 33 | struct.pack('{0}B'.format(len(parsed)), *parsed), 'utf-8') 34 | except UnicodeDecodeError: 35 | APP_CONTEXT.get_logger().logger.error( 36 | 'Parse data as string failed') 37 | formatted = '' 38 | 39 | return formatted 40 | 41 | 42 | def _need_check(limit_type, device_type): 43 | if limit_type is None: 44 | return True 45 | 46 | return limit_type == device_type 47 | 48 | 49 | def run_command_as_string(communicator, command): 50 | ''' Run command and parse result as string 51 | ''' 52 | data_buffer = _run_command(communicator, command) 53 | result = _format_string(data_buffer) 54 | 55 | return result 56 | 57 | 58 | def try_parse_app_mode(info_text): 59 | is_app_mode = False 60 | app_ping_info = None 61 | 62 | split_text = info_text.split(' RTK_INS') 63 | 64 | if len(split_text) == 2: 65 | is_app_mode = True 66 | 67 | device_info_text = split_text[0] 68 | app_info_text = 'RTK_INS' + split_text[1] 69 | 70 | app_ping_info = { 71 | 'device_type': 'INS401', 72 | 'device_info': device_info_text, 73 | 'app_info': app_info_text 74 | } 75 | 76 | return is_app_mode, app_ping_info 77 | 78 | 79 | def try_parse_bootloader_mode(info_text): 80 | is_bootloader_mode = False 81 | bootloader_ping_info = None 82 | 83 | split_text = info_text.split('SN:') 84 | print(split_text) 85 | if len(split_text) == 2: 86 | is_bootloader_mode = True 87 | 88 | device_info_text = info_text 89 | app_info_text = info_text 90 | 91 | bootloader_ping_info = { 92 | 'device_type': 'INS401', 93 | 'device_info': device_info_text, 94 | 'app_info': app_info_text 95 | } 96 | 97 | return is_bootloader_mode, bootloader_ping_info 98 | 99 | 100 | def ping(communicator, *args): 101 | '''OpenDevice Ping 102 | ''' 103 | info_text = run_command_as_string(communicator, pG) 104 | 105 | # Prevent action. Get app info again, 106 | # if cannot retrieve any info at the first time of ping. Should find the root cause. 107 | 108 | if info_text.find('INS401') > -1: 109 | is_app_mode, app_ping_info = try_parse_app_mode(info_text) 110 | if is_app_mode: 111 | return app_ping_info 112 | 113 | is_bootloader_mode, bootloader_ping_info = try_parse_bootloader_mode( 114 | info_text) 115 | if is_bootloader_mode: 116 | return bootloader_ping_info 117 | 118 | return None 119 | 120 | return None 121 | -------------------------------------------------------------------------------- /src/aceinna/devices/ping/open.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import struct 4 | from ...framework.utils import (helper, resource) 5 | from ...framework.context import APP_CONTEXT 6 | 7 | pG = 'pG' 8 | gV = 'gV' 9 | 10 | 11 | def _run_command(communicator, command, size, retry): 12 | command_line = helper.build_input_packet(command) 13 | communicator.write(command_line) 14 | time.sleep(0.1) 15 | 16 | data_buffer = helper.read_untils_have_data( 17 | communicator, command, size, retry) 18 | 19 | return data_buffer 20 | 21 | 22 | def _format_string(data_buffer): 23 | parsed = bytearray(data_buffer) if data_buffer and len( 24 | data_buffer) > 0 else None 25 | 26 | formatted = '' 27 | if parsed is not None: 28 | try: 29 | if sys.version_info < (3, 0): 30 | formatted = str(struct.pack( 31 | '{0}B'.format(len(parsed)), *parsed)) 32 | else: 33 | formatted = str(struct.pack( 34 | '{0}B'.format(len(parsed)), *parsed), 'utf-8') 35 | except UnicodeDecodeError: 36 | APP_CONTEXT.get_logger().logger.error('Parse data as string failed') 37 | formatted = '' 38 | 39 | return formatted 40 | 41 | 42 | def _need_check(limit_type, device_type): 43 | if limit_type is None: 44 | return True 45 | 46 | return limit_type == device_type 47 | 48 | 49 | def run_command_as_string(communicator, command, size=1000, retry=10): 50 | ''' Run command and parse result as string 51 | ''' 52 | data_buffer = _run_command(communicator, command, size, retry) 53 | result = _format_string(data_buffer) 54 | 55 | return result 56 | 57 | 58 | def ping(communicator, *args): 59 | '''OpenDevice Ping 60 | ''' 61 | filter_device_type = args[0] 62 | 63 | device_info_text = run_command_as_string(communicator, pG) 64 | app_info_text = run_command_as_string(communicator, gV) 65 | 66 | # Prevent action. Get app info again, 67 | # if cannot retrieve any info at the first time of ping. Should find the root cause. 68 | if app_info_text == '': 69 | app_info_text = run_command_as_string(communicator, gV) 70 | 71 | if _need_check(filter_device_type, 'RTK') and device_info_text.find('OpenRTK') > -1: 72 | return { 73 | 'device_type': 'OpenRTK', 74 | 'device_info': device_info_text, 75 | 'app_info': app_info_text 76 | } 77 | 78 | # a bad check, to distinguish RTK330L or OpenRTK330L 79 | if _need_check(filter_device_type, 'RTKL') and device_info_text.find('RTK330L') > -1 and device_info_text.find('OpenRTK') == -1 : 80 | return { 81 | 'device_type': 'RTKL', 82 | 'device_info': device_info_text, 83 | 'app_info': app_info_text 84 | } 85 | 86 | if _need_check(filter_device_type, 'IMU') and device_info_text.find('OpenIMU') > -1 and \ 87 | device_info_text.find('OpenRTK') == -1: 88 | return { 89 | 'device_type': 'OpenIMU', 90 | 'device_info': device_info_text, 91 | 'app_info': app_info_text 92 | } 93 | if _need_check(filter_device_type, 'INS') and device_info_text.find('INS401') > -1: 94 | return { 95 | 'device_type': 'INS401', 96 | 'device_info': device_info_text, 97 | 'app_info': app_info_text 98 | } 99 | 100 | return None 101 | -------------------------------------------------------------------------------- /src/aceinna/devices/ping/ping_tool.py: -------------------------------------------------------------------------------- 1 | from .dmu import ping as ping_dmu 2 | from .open import ping as ping_opendevice 3 | from .ins2000 import ping as ping_ins2000 4 | from .ins401 import ping as ping_ins401 5 | from ...framework.context import APP_CONTEXT 6 | from ...framework.constants import INTERFACES 7 | 8 | def do_ping(communicator_type, device_access, filter_device_type): 9 | if communicator_type == INTERFACES.UART: 10 | if filter_device_type is None or filter_device_type in ['IMU', 'RTK', 'RTKL']: 11 | APP_CONTEXT.get_logger().logger.debug( 12 | 'Checking if is OpenRTK/OpenIMU/RTK330L device...') 13 | ping_result = ping_opendevice( 14 | device_access, filter_device_type) 15 | if ping_result: 16 | return ping_result 17 | 18 | if filter_device_type is None or filter_device_type == 'DMU': 19 | APP_CONTEXT.get_logger().logger.debug('Checking if is DMU device...') 20 | ping_result = ping_dmu(device_access, filter_device_type) 21 | if ping_result: 22 | return ping_result 23 | 24 | if filter_device_type is None or filter_device_type == 'INS2000': 25 | APP_CONTEXT.get_logger().logger.debug('Checking if is INS2000 device...') 26 | ping_result = ping_ins2000(device_access, filter_device_type) 27 | if ping_result: 28 | return ping_result 29 | 30 | if communicator_type == INTERFACES.ETH: 31 | APP_CONTEXT.get_logger().logger.debug('Checking if is OpenRTK device...') 32 | ping_result = ping_opendevice(device_access, None) 33 | if ping_result: 34 | return ping_result 35 | 36 | if communicator_type == INTERFACES.ETH_100BASE_T1: 37 | APP_CONTEXT.get_logger().logger.debug('Checking if is INS401 device...') 38 | ping_result = ping_ins401(device_access, None) 39 | if ping_result: 40 | return ping_result 41 | 42 | return None 43 | -------------------------------------------------------------------------------- /src/aceinna/devices/rtkl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/devices/rtkl/__init__.py -------------------------------------------------------------------------------- /src/aceinna/devices/upgrade_workers/__init__.py: -------------------------------------------------------------------------------- 1 | class UPGRADE_EVENT: 2 | ''' 3 | Event type of Device Message Center 4 | ''' 5 | FIRST_PACKET = 'first_packet' 6 | BEFORE_WRITE = 'before_write' 7 | AFTER_WRITE = 'after_write' 8 | 9 | BEFORE_COMMAND='before_command' 10 | AFTER_COMMAND='after_command' 11 | 12 | FINISH = 'finish' 13 | ERROR = 'error' 14 | PROGRESS = 'progress' 15 | 16 | 17 | class UPGRADE_GROUP: 18 | FIRMWARE = 'firmware' 19 | BEFORE_ALL = 'before_all' 20 | AFTER_ALL = 'after_all' 21 | 22 | 23 | from .firmware_worker import FirmwareUpgradeWorker 24 | from .ethernet_sdk_9100_worker import SDKUpgradeWorker as EthernetSDK9100UpgradeWorker 25 | from .sdk_8100_worker import SDKUpgradeWorker as SDK8100UpgradeWorker 26 | from .sdk_9100_worker import SDKUpgradeWorker as SDK9100UpgradeWorker 27 | from .sdk_8100Bx_worker import SDKUpgradeWorker as SDK8100BxUpgradeWorker 28 | from .jump_application_worker import JumpApplicationWorker 29 | from .jump_bootloader_worker import JumpBootloaderWorker 30 | 31 | -------------------------------------------------------------------------------- /src/aceinna/devices/upgrade_workers/firmware_worker.py: -------------------------------------------------------------------------------- 1 | import time 2 | from ..base.upgrade_worker_base import UpgradeWorkerBase 3 | from ...framework.utils import helper 4 | from ...framework.command import Command 5 | from . import (UPGRADE_EVENT, UPGRADE_GROUP) 6 | 7 | 8 | class FirmwareUpgradeWorker(UpgradeWorkerBase): 9 | '''Firmware upgrade worker 10 | ''' 11 | 12 | def __init__(self, communicator, file_content, command_generator, block_size=240): 13 | super(FirmwareUpgradeWorker, self).__init__() 14 | self._communicator = communicator 15 | self.current = 0 16 | #self._baudrate = baudrate 17 | self.max_data_len = block_size # custom 18 | self._group = UPGRADE_GROUP.FIRMWARE 19 | 20 | self._command_generator = command_generator 21 | if not callable(file_content): 22 | self._file_content = file_content 23 | else: 24 | self._file_content = file_content() 25 | self.total = len(self._file_content) 26 | 27 | def stop(self): 28 | self._is_stopped = True 29 | 30 | def get_upgrade_content_size(self): 31 | return self.total 32 | 33 | def write_block(self, data_len, current, data): 34 | ''' 35 | Send block to bootloader 36 | ''' 37 | if not callable(self._command_generator): 38 | self.emit(UPGRADE_EVENT.ERROR, self._key, 39 | 'There is no command generator for Firmware upgrade worker.') 40 | return False 41 | 42 | command = self._command_generator(data_len, current, data) 43 | 44 | actual_command = None 45 | payload_length_format = 'B' 46 | listen_packet = 'WA' 47 | 48 | if isinstance(command, Command): 49 | actual_command = command.actual_command 50 | payload_length_format = command.payload_length_format 51 | listen_packet = command.packet_type 52 | 53 | if isinstance(command, list): 54 | actual_command = command 55 | 56 | # helper.build_bootloader_input_packet( 57 | # 'WA', data_len, current, data) 58 | try: 59 | self._communicator.write(actual_command, True) 60 | except Exception as ex: # pylint: disable=broad-except 61 | return False 62 | 63 | # custom 64 | if current == 0: 65 | try: 66 | self.emit(UPGRADE_EVENT.FIRST_PACKET) 67 | except Exception as ex: 68 | self.emit(UPGRADE_EVENT.ERROR, self._key, 69 | 'Fail in first packet: {0}'.format(ex)) 70 | return False 71 | 72 | response = helper.read_untils_have_data( 73 | self._communicator, listen_packet, 12, 200, payload_length_format) 74 | 75 | if response is None: 76 | return False 77 | return True 78 | 79 | def work(self): 80 | '''Upgrades firmware of connected device to file provided in argument 81 | ''' 82 | if self._is_stopped: 83 | return 84 | if self.current == 0 and self.total == 0: 85 | self.emit(UPGRADE_EVENT.ERROR, self._key, 'Invalid file content') 86 | return 87 | 88 | try: 89 | self.emit(UPGRADE_EVENT.BEFORE_WRITE) 90 | except Exception as ex: 91 | self.emit(UPGRADE_EVENT.ERROR, self._key, 92 | 'Fail in before write: {0}'.format(ex)) 93 | return 94 | while self.current < self.total: 95 | if self._is_stopped: 96 | return 97 | 98 | packet_data_len = self.max_data_len if ( 99 | self.total - self.current) > self.max_data_len else (self.total - self.current) 100 | data = self._file_content[self.current: ( 101 | self.current + packet_data_len)] 102 | write_result = self.write_block( 103 | packet_data_len, self.current, data) 104 | 105 | if not write_result: 106 | self.emit(UPGRADE_EVENT.ERROR, self._key, 107 | 'Write firmware operation failed') 108 | return 109 | 110 | self.current += packet_data_len 111 | self.emit(UPGRADE_EVENT.PROGRESS, self._key, 112 | self.current, self.total) 113 | 114 | try: 115 | self.emit(UPGRADE_EVENT.AFTER_WRITE) 116 | except Exception as ex: 117 | self.emit(UPGRADE_EVENT.ERROR, self._key, 118 | 'Fail in after write: {0}'.format(ex)) 119 | return 120 | 121 | if self.total > 0 and self.current >= self.total: 122 | self.emit(UPGRADE_EVENT.FINISH, self._key) 123 | -------------------------------------------------------------------------------- /src/aceinna/devices/upgrade_workers/jump_application_worker.py: -------------------------------------------------------------------------------- 1 | import time 2 | from ..base.upgrade_worker_base import UpgradeWorkerBase 3 | from ...framework.utils import helper 4 | from ...framework.command import Command 5 | from . import (UPGRADE_EVENT, UPGRADE_GROUP) 6 | 7 | 8 | class JumpApplicationWorker(UpgradeWorkerBase): 9 | '''Firmware upgrade worker 10 | ''' 11 | _command = None 12 | _listen_packet = None 13 | _wait_timeout_after_command = 3 14 | 15 | def __init__(self, communicator, *args, **kwargs): 16 | super(JumpApplicationWorker, self).__init__() 17 | self._communicator = communicator 18 | self.current = 0 19 | self.total = 0 20 | self._group = UPGRADE_GROUP.FIRMWARE 21 | 22 | if kwargs.get('command'): 23 | self._command = kwargs.get('command') 24 | 25 | if kwargs.get('listen_packet'): 26 | self._listen_packet = kwargs.get('listen_packet') 27 | 28 | if kwargs.get('wait_timeout_after_command'): 29 | self._wait_timeout_after_command = kwargs.get( 30 | 'wait_timeout_after_command') 31 | 32 | def stop(self): 33 | self._is_stopped = True 34 | 35 | def get_upgrade_content_size(self): 36 | return self.total 37 | 38 | def work(self): 39 | '''Send JA and ping device 40 | ''' 41 | 42 | if self._is_stopped: 43 | return 44 | 45 | if self._command: 46 | actual_command = None 47 | payload_length_format = 'B' 48 | 49 | if callable(self._command): 50 | self._command = self._command() 51 | 52 | if isinstance(self._command, Command): 53 | actual_command = self._command.actual_command 54 | payload_length_format = self._command.payload_length_format 55 | 56 | if isinstance(self._command, list): 57 | actual_command = self._command 58 | 59 | self.emit(UPGRADE_EVENT.BEFORE_COMMAND) 60 | 61 | self._communicator.reset_buffer() 62 | self._communicator.write(actual_command) 63 | 64 | time.sleep(self._wait_timeout_after_command) 65 | 66 | helper.read_untils_have_data( 67 | self._communicator, self._listen_packet, 1000, 50, payload_length_format) 68 | 69 | self.emit(UPGRADE_EVENT.AFTER_COMMAND) 70 | 71 | # ping device 72 | # can_ping = False 73 | # self._communicator.serial_port.baudrate = self._original_baudrate 74 | 75 | # while not can_ping: 76 | # self._communicator.reset_buffer() # clear input and output buffer 77 | # info = ping(self._communicator, None) 78 | # if info: 79 | # can_ping = True 80 | # time.sleep(0.5) 81 | 82 | self.emit(UPGRADE_EVENT.FINISH, self._key) 83 | -------------------------------------------------------------------------------- /src/aceinna/devices/upgrade_workers/jump_bootloader_worker.py: -------------------------------------------------------------------------------- 1 | from array import array 2 | import time 3 | 4 | from ..base.upgrade_worker_base import UpgradeWorkerBase 5 | from ...framework.utils import helper 6 | from ...framework.command import Command 7 | from . import (UPGRADE_EVENT, UPGRADE_GROUP) 8 | 9 | 10 | class JumpBootloaderWorker(UpgradeWorkerBase): 11 | '''Firmware upgrade worker 12 | ''' 13 | _command = None 14 | _listen_packet = None 15 | _wait_timeout_after_command = 3 16 | 17 | def __init__(self, communicator, *args, **kwargs): 18 | super(JumpBootloaderWorker, self).__init__() 19 | self._communicator = communicator 20 | self.current = 0 21 | self.total = 0 22 | self._group = UPGRADE_GROUP.FIRMWARE 23 | 24 | if kwargs.get('command'): 25 | self._command = kwargs.get('command') 26 | 27 | if kwargs.get('listen_packet'): 28 | self._listen_packet = kwargs.get('listen_packet') 29 | 30 | if kwargs.get('wait_timeout_after_command'): 31 | self._wait_timeout_after_command = kwargs.get( 32 | 'wait_timeout_after_command') 33 | 34 | def stop(self): 35 | self._is_stopped = True 36 | 37 | def get_upgrade_content_size(self): 38 | return self.total 39 | 40 | def work(self): 41 | '''Send JI command 42 | ''' 43 | if self._is_stopped: 44 | return 45 | 46 | if self._command: 47 | actual_command = None 48 | payload_length_format = 'B' 49 | 50 | if callable(self._command): 51 | self._command = self._command() 52 | 53 | if isinstance(self._command, Command): 54 | actual_command = self._command.actual_command 55 | payload_length_format = self._command.payload_length_format 56 | 57 | if isinstance(self._command, list): 58 | actual_command = self._command 59 | 60 | self.emit(UPGRADE_EVENT.BEFORE_COMMAND) 61 | 62 | self._communicator.reset_buffer() 63 | self._communicator.write(actual_command) 64 | 65 | time.sleep(self._wait_timeout_after_command) 66 | 67 | response = helper.read_untils_have_data( 68 | self._communicator, self._listen_packet, 1000, 50, payload_length_format) 69 | 70 | self.emit(UPGRADE_EVENT.AFTER_COMMAND) 71 | 72 | self.emit(UPGRADE_EVENT.FINISH, self._key) 73 | -------------------------------------------------------------------------------- /src/aceinna/devices/widgets/__init__.py: -------------------------------------------------------------------------------- 1 | from .ntrip_client import NTRIPClient 2 | from .lan_data_logger import LanDataLogger 3 | from .lan_data_logger import LanDebugDataLogger 4 | from .lan_data_logger import LanRTCMDataLogger 5 | from .odometer_listener import (OdometerListener, CanOptions) 6 | from .ethernet_data_logger import EthernetDataLogger 7 | from .ethernet_data_logger import EthernetDebugDataLogger 8 | from .ethernet_data_logger import EthernetRTCMDataLogger 9 | -------------------------------------------------------------------------------- /src/aceinna/devices/widgets/ethernet_data_logger.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | 4 | class EthernetDataLogger: 5 | def __init__(self, properties, communicator, log_writer): 6 | self.log_writer = log_writer 7 | self.communicator = communicator 8 | 9 | def run(self): 10 | ''' start to log data from Ethernet ''' 11 | print('start to log data from Ethernet\n') 12 | self._read_and_write() 13 | 14 | def _read_and_write(self): 15 | while True: 16 | read_data = self.communicator.read() 17 | if read_data: 18 | self.log_writer.write(read_data) 19 | pass 20 | 21 | class EthernetDebugDataLogger: 22 | def __init__(self, properties, communicator, log_writer): 23 | self.log_writer = log_writer 24 | self.communicator = communicator 25 | 26 | def run(self): 27 | ''' start to log data from lan port ''' 28 | print('start to log debug data from Ethernet\n') 29 | self._read_and_write() 30 | 31 | def _read_and_write(self): 32 | # send get configuration 33 | while True: 34 | try: 35 | read_data = self.communicator.read() 36 | if read_data: 37 | self.log_writer.write(read_data) 38 | except Exception as e: 39 | print('Data Log Failed, exit') 40 | pass 41 | 42 | 43 | class EthernetRTCMDataLogger: 44 | def __init__(self, properties, communicator, log_writer): 45 | self.log_writer = log_writer 46 | self.communicator = communicator 47 | 48 | def run(self): 49 | print('start to log RTCM data from Ethernet\n') 50 | self._read_and_write() 51 | 52 | def _read_and_write(self): 53 | # send get configuration 54 | print('------------------------------------------------------------') 55 | while True: 56 | try: 57 | read_data = self.communicator.read() 58 | if read_data: 59 | self.log_writer.write(read_data) 60 | except Exception as e: 61 | print('Data Log Failed, exit') 62 | pass 63 | -------------------------------------------------------------------------------- /src/aceinna/devices/widgets/lan_data_logger.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | import json 4 | from ...framework.wrapper import SocketConnWrapper 5 | 6 | 7 | class LanDataLogger: 8 | def __init__(self, properties, communicator, log_writer): 9 | self.host = communicator.host 10 | self.port = 2204 11 | self.log_writer = log_writer 12 | self.log_conn = None 13 | self.sock = None 14 | 15 | def run(self): 16 | ''' start to log data from lan port ''' 17 | self._connect() 18 | self._read_and_write() 19 | 20 | def _connect(self): 21 | # establish TCP Server 22 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 23 | self.sock.bind((self.host, self.port)) 24 | self.sock.listen(5) 25 | 26 | # wait for client 27 | conn, addr = self.sock.accept() 28 | self.log_conn = SocketConnWrapper(conn) 29 | 30 | def _read_and_write(self): 31 | # send get configuration 32 | if self.log_conn is None: 33 | return 34 | #print('0000000000000000000000000000000000000000000000000000000') 35 | self.log_conn.write('get configuration\r\n'.encode()) 36 | try_times = 10 37 | for _ in range(try_times): 38 | data_buffer = self.log_conn.read(500) 39 | if data_buffer is not None: 40 | try: 41 | str_data = bytes.decode(data_buffer) 42 | json_data = json.loads(str_data) 43 | for key in json_data.keys(): 44 | if key == 'openrtk configuration': 45 | self.log_writer.write(data_buffer) 46 | break 47 | break 48 | except Exception as e: 49 | pass 50 | 51 | self.log_conn.write('log debug on\r\n'.encode()) 52 | while True: 53 | try: 54 | read_data = self.log_conn.read(1024) 55 | self.log_writer.write(read_data) 56 | except Exception as e: 57 | print('Data Log Failed, exit') 58 | 59 | class LanDebugDataLogger: 60 | def __init__(self, properties, communicator, log_writer): 61 | self.host = communicator.host 62 | self.port = 2240 63 | self.log_writer = log_writer 64 | self.log_conn = None 65 | self.sock = None 66 | 67 | def run(self): 68 | ''' start to log data from lan port ''' 69 | self._connect() 70 | self._read_and_write() 71 | 72 | def _connect(self): 73 | # establish TCP Server 74 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 75 | self.sock.bind((self.host, self.port)) 76 | self.sock.listen(5) 77 | 78 | # wait for client 79 | conn, addr = self.sock.accept() 80 | self.log_conn = SocketConnWrapper(conn) 81 | 82 | def _read_and_write(self): 83 | # send get configuration 84 | if self.log_conn is None: 85 | return 86 | while True: 87 | try: 88 | read_data = self.log_conn.read(1024) 89 | self.log_writer.write(read_data) 90 | except Exception as e: 91 | print('Data Log Failed, exit') 92 | 93 | 94 | class LanRTCMDataLogger: 95 | def __init__(self, properties, communicator, log_writer): 96 | self.host = communicator.host 97 | self.port = 2230 98 | self.log_writer = log_writer 99 | self.log_conn = None 100 | self.sock = None 101 | 102 | def run(self): 103 | #print('start to log data from lan port') 104 | self._connect() 105 | self._read_and_write() 106 | 107 | def _connect(self): 108 | # establish TCP Server 109 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 110 | self.sock.bind((self.host, self.port)) 111 | self.sock.listen(5) 112 | #print('start to connect rtcm port') 113 | # wait for client 114 | conn, addr = self.sock.accept() 115 | self.log_conn = SocketConnWrapper(conn) 116 | #print('connect rtcm port') 117 | 118 | def _read_and_write(self): 119 | if self.log_conn is None: 120 | return 121 | while True: 122 | try: 123 | read_data = self.log_conn.read(1024) 124 | self.log_writer.write(read_data) 125 | except Exception as e: 126 | print('Data Log Failed, exit') 127 | -------------------------------------------------------------------------------- /src/aceinna/devices/widgets/odometer_listener.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import can 4 | import time 5 | import threading 6 | import random 7 | from ...core.event_base import EventBase 8 | 9 | 10 | def parse_wheel_speed(data): 11 | ''' 12 | Parse WHEEL_SPEEDS info from Toyota Corolla. 13 | 14 | in: CAN msg 15 | out: in [km/h] 16 | WHEEL_SPEED_FR 17 | WHEEL_SPEED_FL 18 | WHEEL_SPEED_RR 19 | WHEEL_SPEED_RL 20 | 21 | dbc: MSB, unsigned 22 | BO_ 170 WHEEL_SPEEDS: 8 XXX 23 | SG_ WHEEL_SPEED_FR : 7|16@0+ (0.01,-67.67) [0|250] "kph" XXX 24 | SG_ WHEEL_SPEED_FL : 23|16@0+ (0.01,-67.67) [0|250] "kph" XXX 25 | SG_ WHEEL_SPEED_RR : 39|16@0+ (0.01,-67.67) [0|250] "kph" XXX 26 | SG_ WHEEL_SPEED_RL : 55 27 | |16@0+ (0.01,-67.67) [0|250] "kph" XXX 28 | ''' 29 | offset = -67.67 30 | speed_fr = (data[0] * 256 + data[1]) * 0.01 + offset 31 | speed_fl = (data[2] * 256 + data[3]) * 0.01 + offset 32 | speed_rr = (data[4] * 256 + data[5]) * 0.01 + offset 33 | speed_rl = (data[6] * 256 + data[7]) * 0.01 + offset 34 | return (speed_fr, speed_fl, speed_rr, speed_rl) 35 | 36 | 37 | def parse_msg(message_type, data): 38 | parse_result = None 39 | if message_type == 'WHEEL_SPEED': 40 | parse_result = parse_wheel_speed(data) 41 | 42 | if not parse_result: 43 | return True, None 44 | 45 | return False, parse_result 46 | 47 | 48 | class CanOptions: 49 | _channel: str 50 | _bitrate: int 51 | 52 | def __init__(self, channel: str, bitrate: int) -> None: 53 | self._channel = channel 54 | self._bitrate = bitrate 55 | 56 | @property 57 | def channel(self): 58 | return self._channel 59 | 60 | @property 61 | def bitrate(self): 62 | return self._bitrate 63 | 64 | 65 | class mock_can_message: 66 | arbitration_id = 0 67 | data = [] 68 | 69 | 70 | def mock_speed_message(): 71 | speed_data = [] 72 | for _ in range(8): 73 | speed_data.append(random.randint(1, 255)) 74 | 75 | msg = mock_can_message() 76 | msg.arbitration_id = 0xAA 77 | msg.data = speed_data 78 | return msg 79 | 80 | 81 | is_linux = sys.platform == 'linux' 82 | 83 | 84 | class OdometerListener(EventBase): 85 | def __init__(self, options: CanOptions): 86 | super(OdometerListener, self).__init__() 87 | 88 | if not is_linux: 89 | return 90 | 91 | # close can0 92 | os.system('sudo ifconfig {0} down'.format(options.channel)) 93 | # set bitrate of can0 94 | os.system('sudo ip link set {0} type can bitrate {1}'.format( 95 | options.channel, options.bitrate)) 96 | # open can0 97 | os.system('sudo ifconfig {0} up'.format(options.channel)) 98 | # os.system('sudo /sbin/ip link set can0 up type can bitrate 250000') 99 | # show details can0 for debug. 100 | # os.system('sudo ip -details link show can0') 101 | 102 | # set up can interface. 103 | # socketcan_native socketcan_ctypes 104 | self.can0 = can.interface.Bus( 105 | channel=options.channel, bustype='socketcan_ctypes') 106 | # set up Notifier 107 | self.notifier = can.Notifier(self.can0, [self.msg_handler]) 108 | 109 | def msg_handler(self, msg): 110 | if msg.arbitration_id == 0xAA: 111 | parse_error, parse_result = parse_msg('WHEEL_SPEED', msg.data) 112 | if parse_error: 113 | return 114 | self.emit('data', parse_result) 115 | 116 | # def __init__(self, options: CanOptions): 117 | # super(OdometerListener, self).__init__() 118 | # threading.Thread(target=self._receive).start() 119 | 120 | # def _receive(self): 121 | # while True: 122 | # message = mock_speed_message() 123 | # if message.arbitration_id == 0xAA: 124 | # parse_error, parse_result = parse_msg('WHEEL_SPEED', message.data) 125 | # if parse_error: 126 | # return 127 | # self.emit('data', parse_result) 128 | # time.sleep(0.001) 129 | -------------------------------------------------------------------------------- /src/aceinna/executor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Development Entry 3 | """ 4 | import os 5 | import sys 6 | import signal 7 | import time 8 | from aceinna.bootstrap import Loader 9 | from aceinna.framework.decorator import ( 10 | receive_args, handle_application_exception) 11 | 12 | IS_WINDOWS = sys.platform.__contains__( 13 | 'win32') or sys.platform.__contains__('win64') 14 | IS_LATER_PY_38 = sys.version_info > (3, 8) 15 | 16 | 17 | def kill_app(signal_int, call_back): 18 | '''Kill main thread 19 | ''' 20 | os.kill(os.getpid(), signal.SIGTERM) 21 | 22 | 23 | @handle_application_exception 24 | @receive_args 25 | def from_command_line(**kwargs): 26 | ''' 27 | Work as command line, with WebSocket and UART 28 | ''' 29 | sub_command = kwargs['options'].sub_command 30 | option_mode = 'cli' 31 | 32 | if sub_command == 'parse': 33 | option_mode = 'log-parser' 34 | 35 | application = Loader.create(option_mode, vars(kwargs['options'])) 36 | application.listen() 37 | 38 | 39 | @handle_application_exception 40 | @receive_args 41 | def start_app(**kwargs): 42 | ''' 43 | Start the application with specified parameters 44 | ''' 45 | sub_command = kwargs['options'].sub_command 46 | option_mode = 'default' 47 | 48 | if kwargs['options'].use_cli: 49 | option_mode = 'cli' 50 | 51 | if sub_command == 'parse': 52 | option_mode = 'log-parser' 53 | 54 | application = Loader.create(option_mode, vars(kwargs['options'])) 55 | application.listen() 56 | 57 | 58 | if __name__ == '__main__': 59 | signal.signal(signal.SIGINT, kill_app) 60 | # compatible code for windows python 3.8 61 | if IS_WINDOWS and IS_LATER_PY_38: 62 | import asyncio 63 | asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) 64 | 65 | start_app() 66 | 67 | while True: 68 | time.sleep(10) 69 | -------------------------------------------------------------------------------- /src/aceinna/framework/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from .app_logger import AppLogger 3 | 4 | # disable logging from scapy 5 | logging.getLogger("scapy").setLevel(logging.CRITICAL) 6 | -------------------------------------------------------------------------------- /src/aceinna/framework/ans_platform_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | from .configuration import get_config 4 | 5 | 6 | class AnsPlatformAPI: 7 | def __init__(self): 8 | self.access_token = '' 9 | self.host_url = get_config().ANS_PLATFORM_URL 10 | 11 | def set_access_token(self, token): 12 | self.access_token = token 13 | 14 | def get_sas_token(self): 15 | try: 16 | url = self.host_url + "token/storagesas" 17 | headers = {'Content-type': 'application/json', 18 | 'Authorization': self.access_token} 19 | response = requests.post(url, headers=headers) 20 | rev = response.json() 21 | if 'token' in rev: 22 | return rev['token'] 23 | else: 24 | return '' 25 | except Exception as ex: 26 | print('Exception when get_sas_token:', ex) 27 | return '' 28 | 29 | def save_backup_restult(self, serial_num, file_name, device_type): 30 | ''' save backup result 31 | ''' 32 | body = { 33 | 'data': { 34 | 'sn': serial_num, 35 | 'file': file_name, 36 | 'type': device_type 37 | } 38 | } 39 | try: 40 | url = self.host_url + "api/userDevices/backup" 41 | data_json = json.dumps(body) 42 | headers = {'Content-type': 'application/json', 43 | 'Authorization': self.access_token} 44 | response = requests.post(url, data=data_json, headers=headers) 45 | return response.json() 46 | except Exception as ex: 47 | # TODO: use logger 48 | print('Exception when update db:', ex) 49 | 50 | def log_device_connection(self, sessionId, device_info): 51 | ''' log device connection to db 52 | ''' 53 | body = { 54 | 'data':{ 55 | 'sessionId': sessionId, 56 | 'device': device_info 57 | } 58 | } 59 | try: 60 | url = self.host_url + "api/deviceConnections/log" 61 | data_json = json.dumps(body) 62 | headers = {'Content-type': 'application/json', 63 | 'Authorization': self.access_token} 64 | response = requests.post(url, data=data_json, headers=headers) 65 | except Exception as ex: 66 | # TODO: use logger 67 | print('Exception when log device connection to db:', ex) 68 | 69 | def save_record_log(self, data): 70 | ''' save record log 71 | ''' 72 | try: 73 | url = self.host_url + "api/recordLogs/post" 74 | data_json = json.dumps(data) 75 | headers = {'Content-type': 'application/json', 76 | 'Authorization': self.access_token} 77 | response = requests.post(url, data=data_json, headers=headers) 78 | return True if 'success' in response.json() else False 79 | except Exception as ex: 80 | # TODO: use logger 81 | print('Exception when save record log:', ex) 82 | -------------------------------------------------------------------------------- /src/aceinna/framework/app_logger.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from logging import handlers 4 | 5 | 6 | class MessageStoreHandler(logging.Handler): 7 | def __init__(self, store): 8 | logging.Handler.__init__(self) 9 | self._store = store 10 | 11 | def mapLogRecord(self, record): 12 | return record.__dict__ 13 | 14 | def emit(self, record): 15 | msg = self.mapLogRecord(record) 16 | self._store.append(msg) 17 | 18 | 19 | class AppLogger(object): 20 | level_relations = { 21 | 'debug': logging.DEBUG, 22 | 'info': logging.INFO, 23 | 'warning': logging.WARNING, 24 | 'error': logging.ERROR, 25 | 'crit': logging.CRITICAL 26 | } 27 | 28 | def __init__(self, filename, level='info', when='D', 29 | backCount=1, gen_file=False, console_log=False, 30 | fmt='%(asctime)s - %(levelname)s: %(message)s'): 31 | # fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'): 32 | self._store = None 33 | self.logger = logging.getLogger(__file__) 34 | self.format_str = logging.Formatter(fmt) 35 | self.logger.setLevel(self.level_relations.get(level)) 36 | self.logger.propagate = False 37 | if console_log: 38 | console_output = logging.StreamHandler() 39 | console_output.setFormatter(self.format_str) 40 | self.logger.addHandler(console_output) 41 | 42 | if gen_file: 43 | folder_path = os.path.dirname(filename) 44 | if not os.path.exists(folder_path): 45 | os.makedirs(folder_path) 46 | daily_file_output = handlers.TimedRotatingFileHandler( 47 | filename=filename, when=when, backupCount=backCount, encoding='utf-8') 48 | daily_file_output.setFormatter(self.format_str) 49 | self.logger.addHandler(daily_file_output) 50 | 51 | def enable_msg_store_handler(self, store): 52 | if not self._store: 53 | self._store = MessageStoreHandler(store) 54 | self._store.setFormatter(self.format_str) 55 | self.logger.addHandler(self._store) 56 | 57 | def info(self, msg, *args, **kwargs): 58 | self.logger.info(msg, *args, **kwargs) 59 | 60 | def debug(self, msg, *args, **kwargs): 61 | self.logger.debug(msg, *args, **kwargs) 62 | 63 | def warning(self, msg, *args, **kwargs): 64 | self.logger.warning(msg, *args, **kwargs) 65 | 66 | def error(self, msg, *args, **kwargs): 67 | self.logger.error(msg, *args, **kwargs) 68 | 69 | def critical(self, msg, *args, **kwargs): 70 | self.logger.critical(msg, *args, **kwargs) 71 | -------------------------------------------------------------------------------- /src/aceinna/framework/command.py: -------------------------------------------------------------------------------- 1 | class Command(object): 2 | _actual_command = [] 3 | _payload_length_format = 'B' 4 | _packet_type = [] 5 | 6 | def __init__(self, packet_type, command_line, payload_length_format='B'): 7 | self._actual_command = command_line 8 | self._payload_length_format = payload_length_format 9 | self._packet_type = packet_type 10 | 11 | @property 12 | def actual_command(self): 13 | return self._actual_command 14 | 15 | @property 16 | def payload_length_format(self): 17 | return self._payload_length_format 18 | 19 | @property 20 | def packet_type(self): 21 | return self._packet_type 22 | 23 | @packet_type.setter 24 | def packet_type(self,value): 25 | self._packet_type = value -------------------------------------------------------------------------------- /src/aceinna/framework/communicator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Communicator 3 | """ 4 | import os 5 | from abc import ABCMeta, abstractmethod 6 | from ..devices import DeviceManager 7 | from .constants import (BAUDRATE_LIST, INTERFACES) 8 | from .context import APP_CONTEXT 9 | from .utils.resource import get_executor_path 10 | 11 | 12 | class CommunicatorFactory: 13 | ''' 14 | Communicator Factory 15 | ''' 16 | @staticmethod 17 | def create(method, options): 18 | ''' 19 | Initial communicator instance 20 | ''' 21 | if method == INTERFACES.UART: 22 | from .communicators import SerialPort 23 | return SerialPort(options) 24 | elif method == INTERFACES.ETH: 25 | from .communicators import LAN 26 | return LAN(options) 27 | elif method == INTERFACES.ETH_100BASE_T1: 28 | from .communicators import Ethernet 29 | return Ethernet(options) 30 | else: 31 | raise Exception('no matched communicator') 32 | 33 | 34 | class Communicator(object): 35 | '''Communicator base 36 | ''' 37 | __metaclass__ = ABCMeta 38 | 39 | def __init__(self): 40 | executor_path = get_executor_path() 41 | setting_folder_name = 'setting' 42 | self.setting_folder_path = os.path.join( 43 | executor_path, setting_folder_name) 44 | self.connection_file_path = os.path.join( 45 | self.setting_folder_path, 'connection.json') 46 | self.read_size = 0 47 | self.device = None 48 | self.threadList = [] 49 | self.type = 'Unknown' 50 | 51 | @abstractmethod 52 | def find_device(self, callback, retries=0, not_found_handler=None): 53 | ''' 54 | find device, then invoke callback 55 | ''' 56 | 57 | def open(self): 58 | ''' 59 | open 60 | ''' 61 | 62 | def close(self): 63 | ''' 64 | close 65 | ''' 66 | 67 | def write(self, data, is_flush=False): 68 | ''' 69 | write 70 | ''' 71 | 72 | def read(self, size): 73 | ''' 74 | read 75 | ''' 76 | 77 | def reset_buffer(self): 78 | ''' 79 | reset data in buffer area 80 | ''' 81 | 82 | def confirm_device(self, *args): 83 | ''' 84 | validate the connected device 85 | ''' 86 | device = None 87 | try: 88 | device = DeviceManager.ping(self, *args) 89 | except Exception as ex: 90 | APP_CONTEXT.get_logger().logger.info('Error while confirm device %s', ex) 91 | device = None 92 | if device and not self.device: 93 | self.device = device 94 | return True 95 | return False 96 | 97 | -------------------------------------------------------------------------------- /src/aceinna/framework/communicators/__init__.py: -------------------------------------------------------------------------------- 1 | from .serialport import SerialPort 2 | from .lan import LAN 3 | from .ethernet_100base_t1 import Ethernet 4 | -------------------------------------------------------------------------------- /src/aceinna/framework/communicators/netbios.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | 5 | class netbios_query: 6 | def __init__(self, name, ip_address_list): 7 | self.__name = name 8 | self.__ip_address_list = ip_address_list 9 | self.__boardcast_address_list = [] 10 | 11 | self.__populate() 12 | 13 | def __populate(self): 14 | for ip_address in self.__ip_address_list: 15 | split_items = ip_address.split('.') 16 | split_items[3] = '255' 17 | boardcast_address = '.'.join(split_items) 18 | self.__boardcast_address_list.append(boardcast_address) 19 | 20 | self.__port = 137 21 | self.__nqs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 22 | self.__nqs.setblocking(False) 23 | self.__query_data = [ 24 | b"\xa9\xfb", # Transaction ID 25 | b"\x01\x10", # Flags Query 26 | b"\x00\x01", # Question:1 27 | b"\x00\x00", # Answer RRS 28 | b"\x00\x00", # Authority RRS 29 | b"\x00\x00", # Additional RRS 30 | b"\x20", # length of Name:32 31 | b"NAME", # Name 32 | b"\x00", # NameNull 33 | b"\x00\x20", # Query Type:NB 34 | b"\x00\x01"] # Class 35 | self.__query_data[7] = str.encode(self.__netbios_encode(self.__name)) 36 | 37 | def __netbios_encode(self, src): 38 | src = src.ljust(15, "\x20") 39 | src = src.ljust(16, "\x00") 40 | names = [] 41 | for c in src: 42 | char_ord = ord(c) 43 | high_4_bits = char_ord >> 4 44 | low_4_bits = char_ord & 0x0f 45 | names.append(high_4_bits) 46 | names.append(low_4_bits) 47 | res = "" 48 | for name in names: 49 | res += chr(0x41+name) 50 | return res 51 | 52 | def query(self): 53 | wait_count = 10 54 | send_data = [] 55 | ret = None 56 | 57 | for bytes_ele in self.__query_data: 58 | for list_ele in bytes_ele: 59 | send_data.append(list_ele) 60 | 61 | while wait_count: 62 | for idx, boardcast_address in enumerate(self.__boardcast_address_list): 63 | try: 64 | self.__nqs.sendto(bytes(send_data), 65 | (boardcast_address, self.__port)) 66 | time.sleep(1) 67 | data_rev, ADDR = self.__nqs.recvfrom(1024) 68 | except Exception as ex: 69 | continue 70 | 71 | if len(data_rev) > 0: 72 | ret = self.__ip_address_list[idx] 73 | break 74 | 75 | if ret: 76 | break 77 | else: 78 | wait_count -= 1 79 | 80 | self.__nqs.close() 81 | 82 | return ret 83 | -------------------------------------------------------------------------------- /src/aceinna/framework/configuration.py: -------------------------------------------------------------------------------- 1 | from .utils.resource import is_dev_mode 2 | from .utils.helper import dict_to_object 3 | 4 | DEV_CONFIG = dict_to_object({ 5 | 'ANS_PLATFORM_URL': 'http://localhost:3000/', 6 | 'AZURE_STORAGE_ACCOUNT': 'navview', 7 | 'AZURE_STORAGE_BACKUP_CONTAINER': 'testing', 8 | 'AZURE_STORAGE_DATA_CONTAINER': 'data-1000', 9 | 'AZURE_STORAGE_APPS_CONTAINER': 'apps' 10 | }) 11 | 12 | PROD_CONFIG = dict_to_object({ 13 | 'ANS_PLATFORM_URL': 'https://api.aceinna.com/', 14 | 'AZURE_STORAGE_ACCOUNT': 'navview', 15 | 'AZURE_STORAGE_BACKUP_CONTAINER': 'backup', 16 | 'AZURE_STORAGE_DATA_CONTAINER': 'data', 17 | 'AZURE_STORAGE_APPS_CONTAINER': 'apps' 18 | }) 19 | 20 | 21 | def get_config(): 22 | ''' 23 | Get configuration by dev mode 24 | ''' 25 | if is_dev_mode(): 26 | return DEV_CONFIG 27 | 28 | return PROD_CONFIG 29 | -------------------------------------------------------------------------------- /src/aceinna/framework/constants.py: -------------------------------------------------------------------------------- 1 | # Device 2 | DEVICE_TYPES = ['IMU', 'RTK', 'DMU'] 3 | BAUDRATE_LIST = [460800, 115200, 57600, 230400, 38400] 4 | DEFAULT_PORT_RANGE = [8000, 8001, 8002, 8003] 5 | 6 | class APP_TYPE: 7 | DEFAULT = 'default' 8 | CLI = 'cli' 9 | RECEIVER = 'receiver' 10 | LOG_PARSER = 'log-parser' 11 | 12 | 13 | class INTERFACES(object): 14 | UART = 'uart' 15 | ETH = 'eth' 16 | ETH_100BASE_T1 = '100base-t1' 17 | 18 | def list(): 19 | return [INTERFACES.UART, INTERFACES.ETH, INTERFACES.ETH_100BASE_T1] 20 | -------------------------------------------------------------------------------- /src/aceinna/framework/context.py: -------------------------------------------------------------------------------- 1 | """ 2 | Context 3 | """ 4 | from .app_logger import AppLogger 5 | from ..core.packet_statistics import PacketStatistics 6 | 7 | 8 | class AppContext: 9 | ''' 10 | App Context 11 | ''' 12 | _logger = None 13 | _print_logger = None 14 | _device_context = None 15 | _statistics = None 16 | _mode = None 17 | _para_path = None 18 | 19 | def __init__(self): 20 | pass 21 | 22 | def set_logger(self, logger): 23 | ''' 24 | logger setter 25 | ''' 26 | self._logger = logger 27 | 28 | def get_logger(self): 29 | ''' 30 | logger getter 31 | ''' 32 | if not self._logger: 33 | self._logger = AppLogger(filename='default') 34 | return self._logger 35 | 36 | def set_print_logger(self, logger): 37 | self._print_logger = logger 38 | 39 | def get_print_logger(self): 40 | if not self._print_logger: 41 | self._print_logger = AppLogger(filename='default') 42 | return self._print_logger 43 | 44 | @property 45 | def device_context(self): 46 | ''' Retrieve device context 47 | ''' 48 | return self._device_context 49 | 50 | @device_context.setter 51 | def device_context(self, value): 52 | self._device_context = value 53 | 54 | @property 55 | def statistics(self): 56 | ''' Retrieve statistics service 57 | ''' 58 | if not self._statistics: 59 | self._statistics = PacketStatistics() 60 | 61 | return self._statistics 62 | 63 | @property 64 | def mode(self): 65 | ''' Retrieve application mode. `default`, `cli` or `log_parser` 66 | ''' 67 | return self._mode 68 | 69 | @mode.setter 70 | def mode(self, value): 71 | self._mode = value 72 | 73 | @property 74 | def para_path(self): 75 | return self._para_path 76 | 77 | @para_path.setter 78 | def para_path(self, value): 79 | self._para_path = value 80 | 81 | APP_CONTEXT = AppContext() 82 | -------------------------------------------------------------------------------- /src/aceinna/framework/decorator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | import traceback 5 | import signal 6 | from datetime import datetime, timedelta 7 | from functools import wraps 8 | from typing import TypeVar 9 | from .constants import (DEVICE_TYPES, BAUDRATE_LIST, INTERFACES) 10 | from .utils.print import print_red 11 | from .utils.resource import is_dev_mode 12 | 13 | 14 | T = TypeVar('T') 15 | 16 | INTERFACE_LIST = INTERFACES.list() 17 | MODES = ['default', 'cli', 'receiver'] 18 | TYPES_OF_LOG = ['openrtk', 'rtkl', 'ins401'] 19 | KML_RATES = [1, 2, 5, 10] 20 | 21 | 22 | def _build_args(): 23 | """parse input arguments 24 | """ 25 | parser = argparse.ArgumentParser( 26 | description='Aceinna python driver input args command:', allow_abbrev=False) 27 | 28 | parser.add_argument("-i", "--interface", dest="interface", metavar='', 29 | help="Interface. Allowed one of values: {0}".format(INTERFACE_LIST), default=INTERFACES.UART, choices=INTERFACE_LIST) 30 | parser.add_argument("-p", "--port", dest='port', metavar='', type=int, 31 | help="Webserver port") 32 | parser.add_argument("--device-type", dest="device_type", type=str, 33 | help="Open Device Type. Allowed one of values: {0}".format(DEVICE_TYPES), choices=DEVICE_TYPES, metavar='') 34 | parser.add_argument("-b", "--baudrate", dest="baudrate", type=int, metavar='', 35 | help="Baudrate for uart. Allowed one of values: {0}".format(BAUDRATE_LIST), choices=BAUDRATE_LIST) 36 | parser.add_argument("-c", "--com-port", dest="com_port", metavar='', type=str, 37 | help="COM Port") 38 | parser.add_argument("--console-log", dest='console_log', action='store_true', 39 | help="Output log on console", default=False) 40 | parser.add_argument("--debug", dest='debug', action='store_true', 41 | help="Log debug information", default=False) 42 | parser.add_argument("--with-data-log", dest='with_data_log', action='store_true', 43 | help="Contains internal data log (OpenIMU only)", default=False) 44 | parser.add_argument("-s", "--set-user-para", dest='set_user_para', action='store_true', 45 | help="Set user parameters (OpenRTK only)", default=False) 46 | parser.add_argument("--para-path", dest="para_path", type=str, 47 | metavar='') 48 | parser.add_argument("--cli", dest='use_cli', action='store_true', 49 | help="start as cli mode", default=False) 50 | 51 | subparsers = parser.add_subparsers( 52 | title='Sub commands', help='use ` -h` to get sub command help', dest="sub_command") 53 | parse_log_action = subparsers.add_parser( 54 | 'parse', help='A parse log command') 55 | parse_log_action.add_argument("-t", metavar='', type=str, 56 | help="Type of logs, Allowed one of values: {0}".format( 57 | TYPES_OF_LOG), 58 | default='openrtk', dest="log_type", choices=TYPES_OF_LOG) 59 | parse_log_action.add_argument( 60 | "-p", type=str, help="The folder path of logs", default='./data', metavar='', dest="path") 61 | parse_log_action.add_argument( 62 | "-i", type=int, help="Ins kml rate(hz). Allowed one of values: {0}".format(KML_RATES), default=5, metavar='', dest="kml_rate", choices=KML_RATES) 63 | 64 | return parser.parse_args() 65 | 66 | 67 | def receive_args(func): 68 | ''' 69 | build arguments in options 70 | ''' 71 | @wraps(func) 72 | def decorated(*args, **kwargs): 73 | options = _build_args() 74 | kwargs['options'] = options 75 | func(*args, **kwargs) 76 | return decorated 77 | 78 | 79 | def handle_application_exception(func): 80 | ''' 81 | add exception handler 82 | ''' 83 | @wraps(func) 84 | def decorated(*args, **kwargs): 85 | try: 86 | func(*args, **kwargs) 87 | except KeyboardInterrupt: # response for KeyboardInterrupt such as Ctrl+C 88 | print('User stop this program by KeyboardInterrupt! File:[{0}], Line:[{1}]'.format( 89 | __file__, sys._getframe().f_lineno)) 90 | os.kill(os.getpid(), signal.SIGTERM) 91 | sys.exit() 92 | except Exception as ex: # pylint: disable=bare-except 93 | if is_dev_mode(): 94 | traceback.print_exc() # For development 95 | print_red('Application Exit Exception: {0}'.format(ex)) 96 | os._exit(1) 97 | return decorated 98 | 99 | 100 | def skip_error(T: type): 101 | ''' 102 | add websocket error handler 103 | ''' 104 | def outer(func): 105 | @wraps(func) 106 | def decorated(*args, **kwargs): 107 | try: 108 | func(*args, **kwargs) 109 | except T: 110 | pass 111 | return decorated 112 | return outer 113 | 114 | 115 | def throttle(seconds=0, minutes=0, hours=0): 116 | throttle_period = timedelta(seconds=seconds, minutes=minutes, hours=hours) 117 | 118 | def throttle_decorator(fn): 119 | time_of_last_call = datetime.min 120 | 121 | @wraps(fn) 122 | def wrapper(*args, **kwargs): 123 | nonlocal time_of_last_call 124 | now = datetime.now() 125 | if now - time_of_last_call > throttle_period: 126 | time_of_last_call = now 127 | return fn(*args, **kwargs) 128 | return wrapper 129 | return throttle_decorator 130 | -------------------------------------------------------------------------------- /src/aceinna/framework/progress_bar.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class ProgressBar(): 5 | """ 6 | Display progress bar 7 | """ 8 | current = 0 # current step 9 | max_steps = 0 # total steps 10 | max_arrow = 50 # length of progress bar 11 | done_info = 'done' 12 | 13 | def __init__(self, total, done_info='Done'): 14 | self.max_steps = total 15 | self.i = 0 16 | self.done_info = done_info 17 | 18 | # [>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]100.00% 19 | def update(self, i=None): 20 | '''update the progress display 21 | ''' 22 | if i is not None: 23 | self.i += i 24 | else: 25 | self.i += 1 26 | # caculate the number of '>' 27 | num_arrow = int(self.i * self.max_arrow / self.max_steps) 28 | num_line = self.max_arrow - num_arrow # caculate the number of '-' 29 | percent = self.i * 100.0 / self.max_steps # caculate the percent 30 | process_bar = '[' + '>' * num_arrow + '-' * num_line + ']'\ 31 | + '%.2f' % percent + '% ' + '\r' 32 | 33 | print(process_bar,end='',flush=True) 34 | if self.i >= self.max_steps: 35 | self.close(True) 36 | 37 | def close(self, print_done_info=False): 38 | '''close print 39 | ''' 40 | if print_done_info: 41 | print(self.done_info) 42 | self.i = 0 43 | -------------------------------------------------------------------------------- /src/aceinna/framework/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/src/aceinna/framework/utils/__init__.py -------------------------------------------------------------------------------- /src/aceinna/framework/utils/dict_extend.py: -------------------------------------------------------------------------------- 1 | class Dict(dict): 2 | __setattr__ = dict.__setitem__ 3 | __getattr__ = dict.__getitem__ -------------------------------------------------------------------------------- /src/aceinna/framework/utils/firmware_parser.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | def parse_data_len(data_len): 5 | '''Parse data length 6 | ''' 7 | return struct.unpack(' 0: 15 | if not serial_port: 16 | try: 17 | serial_port = serial.Serial( 18 | port=port_name, baudrate=baudrate_mapping[device_type]) 19 | except serial.serialutil.SerialException: 20 | serial_port = None 21 | 22 | time.sleep(0.01) 23 | left_time -= 0.01 24 | continue 25 | 26 | # JB commands 27 | serial_port.write([0x55, 0x55, 0x4A, 0x42, 0x00, 0xA0, 0xCE]) 28 | time.sleep(0.02) 29 | left_time -= 0.02 30 | 31 | if serial_port: 32 | serial_port.close() 33 | 34 | return { 35 | 'packetType': 'success', 36 | 'data': { 37 | 'status': 1 38 | } 39 | } 40 | 41 | 42 | if __name__ == '__main__': 43 | process_force_bootloader('/dev/cu.usbserial-AK005M29', 'IMU') 44 | -------------------------------------------------------------------------------- /tests/message_center.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import time 5 | 6 | try: 7 | from aceinna.devices.message_center import DeviceMessageCenter 8 | from aceinna.devices.parser_manager import ParserManager 9 | from aceinna.framework.utils import helper 10 | from mocker.communicator import MockCommunicator 11 | from mocker.devices import helper as test_helper 12 | except: # pylint: disable=bare-except 13 | print('load package from local') 14 | sys.path.append('./src') 15 | # sys.path.append('./tests') 16 | from aceinna.devices.message_center import DeviceMessageCenter 17 | from aceinna.devices.parser_manager import ParserManager 18 | from aceinna.framework.utils import helper 19 | from mocker.communicator import MockCommunicator 20 | from mocker.devices import helper as test_helper 21 | 22 | 23 | global MOCK_COMMUNICATOR 24 | global MESSAGE_CENTER 25 | 26 | MOCK_COMMUNICATOR = False 27 | MESSAGE_CENTER = None 28 | 29 | DEVICE_TYPE = APP_NAME = 'IMU' 30 | APP_FILE_PATH = os.path.join( 31 | os.getcwd(), 'setting', 'openimu', APP_NAME, 'openimu.json') 32 | PROPERTIES = None 33 | with open(APP_FILE_PATH) as json_data: 34 | PROPERTIES = json.load(json_data) 35 | 36 | 37 | def setup_message_center(): 38 | global MESSAGE_CENTER 39 | global MOCK_COMMUNICATOR 40 | 41 | # port = '/dev/cu.usbserial-AK005M29' 42 | # baud = 115200 43 | # uart = serial.Serial(port, baud, timeout=0.1) 44 | MOCK_COMMUNICATOR = MockCommunicator(options={'device': DEVICE_TYPE}) 45 | 46 | MESSAGE_CENTER = DeviceMessageCenter(MOCK_COMMUNICATOR) 47 | parser = ParserManager.build( 48 | DEVICE_TYPE, 'uart', PROPERTIES) 49 | MESSAGE_CENTER.set_parser(parser) 50 | #MESSAGE_CENTER.on('continuous_message', handle_message) 51 | MESSAGE_CENTER.setup() 52 | 53 | 54 | def close_message_center(): 55 | MESSAGE_CENTER.stop() 56 | MOCK_COMMUNICATOR.close() 57 | 58 | 59 | def handle_message(packet_type, data): 60 | # parse continuous data 61 | print(packet_type, data) 62 | 63 | 64 | def receive_messasge(packet_type, data, error): 65 | print(packet_type, data, error) 66 | 67 | 68 | def build_and_send(command, **params): 69 | properties = params.get('properties') 70 | param_id = params.get('param_id') 71 | param_value = params.get('param_value') 72 | delay = params.get('delay') 73 | 74 | cli = helper.build_input_packet( 75 | command, properties=properties, param=param_id, value=param_value) 76 | if delay and delay > 0: 77 | msg = MESSAGE_CENTER.build( 78 | command=test_helper.wrap_delay_response_command(cli, delay), timeout=1) 79 | else: 80 | msg = MESSAGE_CENTER.build(command=cli, timeout=2) 81 | msg.on('finished', receive_messasge) 82 | msg.send() 83 | 84 | 85 | if __name__ == '__main__': 86 | setup_message_center() 87 | 88 | # ping device 89 | build_and_send('pG') 90 | # get device version with delay response 91 | build_and_send('gV', delay=3) 92 | # ping device 93 | build_and_send('gA') 94 | 95 | # read the parameter 96 | build_and_send('gP', properties=PROPERTIES, param_id=3) 97 | # change parameter 98 | build_and_send('uP', properties=PROPERTIES, param_id=3, param_value='s1') 99 | # valid the change 100 | build_and_send('gP', properties=PROPERTIES, param_id=3) 101 | 102 | # send a invalid response command 103 | build_and_send('NA') 104 | # check if still work 105 | build_and_send('gP', properties=PROPERTIES, param_id=3) 106 | 107 | time.sleep(5) 108 | # stop message center after 1s 109 | close_message_center() 110 | -------------------------------------------------------------------------------- /tests/mocker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/tests/mocker/__init__.py -------------------------------------------------------------------------------- /tests/mocker/communicator.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | from aceinna.framework.communicator import Communicator 5 | from aceinna.framework.utils.helper import dict_to_object 6 | from .device_access import DeviceAccess 7 | 8 | 9 | class MockCommunicator(Communicator): 10 | def __init__(self, options=None): 11 | super(MockCommunicator, self).__init__() 12 | self.type = 'uart' 13 | 14 | converted_options = dict_to_object(options) 15 | device_name = getattr(converted_options, 'device', 'IMU') 16 | 17 | self._device_access = DeviceAccess(device_name) 18 | self._device_access.start() 19 | 20 | @property 21 | def device_access(self): 22 | return self._device_access 23 | 24 | def find_device(self, callback): 25 | # confirm device 26 | self.confirm_device(self._device_access, None) 27 | 28 | if self.device: 29 | callback(self.device) 30 | 31 | def open(self): 32 | pass 33 | 34 | def close(self): 35 | self._device_access.stop() 36 | 37 | def write(self, data, is_flush=False): 38 | self._device_access.write(data) 39 | 40 | def read(self, size=100): 41 | return self._device_access.read(size) 42 | -------------------------------------------------------------------------------- /tests/mocker/device_access.py: -------------------------------------------------------------------------------- 1 | import os 2 | import fcntl 3 | import threading 4 | import struct 5 | import termios 6 | import time 7 | 8 | MOTION_DATA = [] 9 | 10 | if hasattr(termios, 'TIOCINQ'): 11 | TIOCINQ = termios.TIOCINQ 12 | else: 13 | TIOCINQ = getattr(termios, 'FIONREAD', 0x541B) 14 | TIOCM_zero_str = struct.pack('I', 0) 15 | 16 | 17 | class DeviceAccess(object): 18 | @property 19 | def port(self): 20 | return self._port 21 | 22 | @port.setter 23 | def port(self, value): 24 | self._port = value 25 | 26 | def __init__(self, app_name): 27 | self._read_buffer = None 28 | self._output_buffer = None 29 | self._command_queue = [] 30 | self._pipe_sensor_data_read = None 31 | self._pipe_sensor_data_write = None 32 | self._pipe_command_read = None 33 | self._pipe_command_write = None 34 | self._generator = None 35 | self._app = None 36 | self._app_name = app_name 37 | self._is_stop = False 38 | self._port = 'test_port_01' 39 | self._load(app_name) 40 | 41 | def start(self): 42 | self._is_stop = False 43 | self._pipe_sensor_data_read, self._pipe_sensor_data_write = os.pipe() 44 | self._pipe_command_read, self._pipe_command_write = os.pipe() 45 | 46 | fcntl.fcntl(self._pipe_sensor_data_read, fcntl.F_SETFL, os.O_NONBLOCK) 47 | fcntl.fcntl(self._pipe_command_read, fcntl.F_SETFL, os.O_NONBLOCK) 48 | 49 | thread = threading.Thread(target=self._run) 50 | thread.start() 51 | 52 | def stop(self): 53 | self._is_stop = True 54 | 55 | def _close_pipe(self): 56 | os.close(self._pipe_sensor_data_read) 57 | os.close(self._pipe_sensor_data_write) 58 | os.close(self._pipe_command_read) 59 | os.close(self._pipe_command_write) 60 | #print('device is closed') 61 | 62 | def _run(self): 63 | print('device is running') 64 | try: 65 | while True: 66 | if self._is_stop: 67 | self._close_pipe() 68 | return 69 | # prepare output data 70 | self._prepare_data() 71 | # receive command, send to handler 72 | self._handle_command() 73 | # run a loop per 10ms, to simulate 100hz output 74 | time.sleep(0.01) 75 | except: 76 | self._close_pipe() 77 | 78 | def _load(self, app_name): 79 | # set application 80 | if app_name == 'IMU': 81 | from mocker.devices.openimu import OpenIMUMocker 82 | cls = OpenIMUMocker 83 | elif app_name == 'RTK': 84 | from mocker.devices.openrtk import OpenRTKMocker 85 | cls = OpenRTKMocker 86 | elif app_name == 'RTKL': 87 | from mocker.devices.rtkl import RTKLMocker 88 | cls = RTKLMocker 89 | elif app_name == 'DMU': 90 | from mocker.devices.dmu import DMUMocker 91 | cls = DMUMocker 92 | else: 93 | raise NotImplementedError("No matched device") 94 | self._app = cls() 95 | self._generator = self._app.gen_sensor_data() 96 | 97 | @property 98 | def in_waiting(self): 99 | """Return the number of bytes currently in the input buffer.""" 100 | s = fcntl.ioctl(self._pipe_command_read, TIOCINQ, TIOCM_zero_str) 101 | return struct.unpack('I', s)[0] 102 | 103 | def _prepare_data(self): 104 | # read motion data, then send to _output_buffer 105 | sensor_data = next(self._generator) 106 | #print('sensor data', len(sensor_data)) 107 | os.write(self._pipe_sensor_data_write, sensor_data) 108 | 109 | def _handle_command(self): 110 | command = None 111 | try: 112 | command = os.read(self._pipe_command_read, self.in_waiting) 113 | except: 114 | command = None 115 | # print('stop',self._is_stop) 116 | if command: 117 | response = None 118 | try: 119 | # do handle command 120 | response = self._app.handle_command(command) 121 | except Exception as ex: 122 | print(ex) 123 | response = None 124 | if response: 125 | # write to response 126 | os.write(self._pipe_sensor_data_write, response) 127 | 128 | def write(self, data): 129 | if isinstance(data, str): 130 | os.write(self._pipe_command_write, data.encode('utf-8')) 131 | else: 132 | os.write(self._pipe_command_write, bytes(data)) 133 | 134 | def read(self, size): 135 | read_result = bytearray() 136 | try: 137 | buf = os.read(self._pipe_sensor_data_read, size) 138 | read_result.extend(buf) 139 | except: 140 | pass 141 | return bytes(read_result) 142 | -------------------------------------------------------------------------------- /tests/mocker/devices/Scale1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/tests/mocker/devices/Scale1.raw -------------------------------------------------------------------------------- /tests/mocker/devices/__init__.py: -------------------------------------------------------------------------------- 1 | class Parameter(object): 2 | def __init__(self, name, data_type, value): 3 | self._name = name 4 | self._data_type = data_type 5 | self._value = value 6 | 7 | @property 8 | def name(self): 9 | return self._name 10 | 11 | @name.setter 12 | def name(self, value): 13 | self._name = value 14 | 15 | @property 16 | def data_type(self): 17 | return self._data_type 18 | 19 | @data_type.setter 20 | def data_type(self, data_type): 21 | self._data_type = data_type 22 | 23 | @property 24 | def value(self): 25 | return self._value 26 | 27 | @value.setter 28 | def value(self, value): 29 | self._value = value 30 | -------------------------------------------------------------------------------- /tests/mocker/devices/base.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class DeviceBase(object): 5 | __metaclass__ = ABCMeta 6 | 7 | def __init__(self): 8 | self._params = None 9 | 10 | @abstractmethod 11 | def handle_command(self, cli): 12 | '''handle command line, and response''' 13 | 14 | @abstractmethod 15 | def gen_sensor_data(self): 16 | '''a generator to prepare sensor packet data''' 17 | 18 | def _find_parameter(self, name): 19 | params_values = self._params.values() 20 | for item in params_values: 21 | if item.name == name: 22 | return item 23 | 24 | return None 25 | -------------------------------------------------------------------------------- /tests/mocker/devices/helper.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | VALID_OPEN_PACKET_TYPES = ['pG', 'uC', 'uP', 'uA', 'uB', 4 | 'sC', 'rD', 5 | 'gC', 'gA', 'gB', 'gV', 'gP', 6 | '\x15\x15', '\x00\x00', 'ma', 'NA', 7 | 'JI', 'JA', 'WA', 8 | 'RE', 'WE', 'UE', 'LE', 'SR'] 9 | 10 | VALID_CLOSURE_PACKET_TYPES = ['PK', 'GP', 'SF', 'WF', 'GF', 'RF', 'RE', 'NA'] 11 | 12 | 13 | def is_command_start(command_start): 14 | return struct.unpack('2B', command_start) == (0x55, 0x55) 15 | 16 | 17 | def is_valid_packet_type(packet_type, is_open): 18 | packet_type = packet_type.decode() 19 | if is_open: 20 | return VALID_OPEN_PACKET_TYPES.__contains__(packet_type) 21 | else: 22 | return VALID_CLOSURE_PACKET_TYPES.__contains__(packet_type) 23 | 24 | 25 | def parse_command_packet(data, is_open=True): 26 | packet_type = '' 27 | payload = [] 28 | error = False 29 | delay = 0 30 | 31 | raw_command_start = data[0:2] 32 | raw_packet_type = data[2:4] 33 | 34 | if is_command_start(raw_command_start) and \ 35 | is_valid_packet_type(raw_packet_type, is_open): 36 | packet_type = raw_packet_type.decode() 37 | payload_len = data[4] # struct.unpack('b', data[4])[0] 38 | payload = data[5:payload_len+5] 39 | crc = data[payload_len+5:payload_len+7] 40 | if payload_len+7 < len(data): 41 | try: 42 | delay_raw = data[payload_len+7:] 43 | delay = struct.unpack('> 8 83 | crc_lsb = (crc & 0x00FF) 84 | return [crc_msb, crc_lsb] 85 | 86 | 87 | def wrap_delay_response_command(cli, delay): 88 | cli.extend(struct.pack("= raw_data_len-1: 63 | break 64 | 65 | return array_data 66 | 67 | 68 | class OpenRTKMocker(DeviceBase): 69 | '''A mocker runing IMU Application''' 70 | 71 | def __init__(self, **kwargs): 72 | super(OpenRTKMocker, self).__init__() 73 | self._ping_str = 'OpenRTK330L OpenIMU330BI 5020-3021-01 1.1.8 SN:1975000034' 74 | self._version_str = 'RTK_INS App v2.0.0, BootLoader v1.1.1' 75 | self._data_index = 0 76 | self._total_data_len = 0 77 | # setup device configuration 78 | self._params = { 79 | '0': Parameter('data_crc', 'uint64', 0), 80 | '1': Parameter('data_size', 'uint64', 0), 81 | '2': Parameter('baud_rate', 'int64', 115200), 82 | '3': Parameter('packet_type', 'char8', 'z1'), 83 | '4': Parameter('packet_rate', 'uint64', 100), 84 | '5': Parameter('accel_lpf', 'int64', 25), 85 | '6': Parameter('rate_lpf', 'int64', 25), 86 | '7': Parameter('orientation', 'char8', '+X+Y+Z') 87 | } 88 | self._prepare_data() 89 | 90 | def _prepare_data(self): 91 | # read the bin, and parse the data in array 92 | 93 | self._sensor_data = { 94 | 'z1': build_sensor_data_array('./tests/mocker/devices/z1.raw'), 95 | 's1': build_sensor_data_array('./tests/mocker/devices/s1.raw') 96 | } 97 | packet_type = self._find_parameter('packet_type').value 98 | self._total_data_len = len(self._sensor_data[packet_type]) 99 | 100 | def handle_command(self, cli): 101 | packet_type, payload, error, delay = parse_command_packet(cli) 102 | output_packet = [] 103 | if delay and delay > 0: 104 | time.sleep(delay) 105 | 106 | if error: 107 | packet_type = '\x00\x00' 108 | output_packet = bytes([]) 109 | 110 | if packet_type == 'pG': 111 | output_packet = pack_str(self._ping_str) 112 | if packet_type == 'gV': 113 | output_packet = pack_str(self._version_str) 114 | if packet_type == 'gA': 115 | for item in self._params: 116 | data_type = self._params[item].data_type 117 | value = self._params[item].value 118 | output_packet.extend(encode_value(data_type, value)) 119 | 120 | output_packet = bytes(output_packet) 121 | if packet_type == 'gP': 122 | param_id = struct.unpack('= self._total_data_len: 155 | self._data_index = 0 156 | -------------------------------------------------------------------------------- /tests/mocker/devices/rtkl.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import time 3 | from . import Parameter 4 | from .base import DeviceBase 5 | from .helper import (parse_command_packet, build_output_packet) 6 | from aceinna.devices.parsers.open_field_parser import ( 7 | decode_value, encode_value) 8 | 9 | 10 | def pack_str(value): 11 | return struct.pack('{0}B'.format( 12 | len(value)), *value.encode('UTF-8')) 13 | 14 | 15 | def build_sensor_data_array(path): 16 | array_data = [] 17 | raw_data = [] 18 | packet_start = [0x55, 0x55] 19 | read_index = 0 20 | is_read_end = False 21 | message = None 22 | 23 | with open(path, 'rb') as data_bytes: 24 | raw_data = data_bytes.read() 25 | 26 | raw_data_len = len(raw_data) 27 | while not is_read_end: 28 | try_packet_start = [ 29 | raw_data[read_index], 30 | raw_data[read_index+1] 31 | ] 32 | 33 | if packet_start == try_packet_start: 34 | # find one sensor data 35 | message = [] 36 | message.extend(try_packet_start) 37 | 38 | # packet type 39 | message.extend(raw_data[read_index+2:read_index+4]) 40 | 41 | # payload length 42 | payload_len = raw_data[read_index+4] 43 | payload_start_index = read_index+5 44 | message.extend([payload_len]) 45 | 46 | # payload 47 | crc_index = payload_start_index+payload_len 48 | 49 | message.extend( 50 | raw_data[payload_start_index: crc_index]) 51 | 52 | # crc 53 | message.extend( 54 | raw_data[crc_index: crc_index+2]) 55 | 56 | array_data.append(message) 57 | 58 | read_index = read_index+len(message) 59 | else: 60 | read_index += 1 61 | 62 | if read_index >= raw_data_len-1: 63 | break 64 | 65 | return array_data 66 | 67 | 68 | class RTKLMocker(DeviceBase): 69 | '''A mocker runing IMU Application''' 70 | 71 | def __init__(self, **kwargs): 72 | super(RTKLMocker, self).__init__() 73 | self._ping_str = 'RTK330LA OpenIMU330BI 5020-3021-01 1.1.8 SN:1975000034' 74 | self._version_str = 'RTK_INS App v2.0.0, BootLoader v1.1.1' 75 | self._data_index = 0 76 | self._total_data_len = 0 77 | # setup device configuration 78 | self._params = { 79 | '0': Parameter('data_crc', 'uint64', 0), 80 | '1': Parameter('data_size', 'uint64', 0), 81 | '2': Parameter('baud_rate', 'int64', 115200), 82 | '3': Parameter('packet_type', 'char8', 'z1'), 83 | '4': Parameter('packet_rate', 'uint64', 100), 84 | '5': Parameter('accel_lpf', 'int64', 25), 85 | '6': Parameter('rate_lpf', 'int64', 25), 86 | '7': Parameter('orientation', 'char8', '+X+Y+Z') 87 | } 88 | self._prepare_data() 89 | 90 | def _prepare_data(self): 91 | # read the bin, and parse the data in array 92 | 93 | self._sensor_data = { 94 | 'z1': build_sensor_data_array('./tests/mocker/devices/z1.raw'), 95 | 's1': build_sensor_data_array('./tests/mocker/devices/s1.raw') 96 | } 97 | packet_type = self._find_parameter('packet_type').value 98 | self._total_data_len = len(self._sensor_data[packet_type]) 99 | 100 | def handle_command(self, cli): 101 | packet_type, payload, error, delay = parse_command_packet(cli) 102 | output_packet = [] 103 | if delay and delay > 0: 104 | time.sleep(delay) 105 | 106 | if error: 107 | packet_type = '\x00\x00' 108 | output_packet = bytes([]) 109 | 110 | if packet_type == 'pG': 111 | output_packet = pack_str(self._ping_str) 112 | if packet_type == 'gV': 113 | output_packet = pack_str(self._version_str) 114 | if packet_type == 'gA': 115 | for item in self._params: 116 | data_type = self._params[item].data_type 117 | value = self._params[item].value 118 | output_packet.extend(encode_value(data_type, value)) 119 | 120 | output_packet = bytes(output_packet) 121 | if packet_type == 'gP': 122 | param_id = struct.unpack('= self._total_data_len: 155 | self._data_index = 0 156 | -------------------------------------------------------------------------------- /tests/mocker/devices/s1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/tests/mocker/devices/s1.raw -------------------------------------------------------------------------------- /tests/mocker/devices/z1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aceinna/python-openimu/4960e9ae0c12ff1bac5ab7ffd6e0992069bd6a1f/tests/mocker/devices/z1.raw -------------------------------------------------------------------------------- /tests/mocker/upgrade_workers/error_worker.py: -------------------------------------------------------------------------------- 1 | from aceinna.devices.base import UpgradeWorkerBase 2 | 3 | 4 | class ErrorWorker(UpgradeWorkerBase): 5 | def __init__(self): 6 | super(ErrorWorker, self).__init__() 7 | self._total = 1024*1024 # 1M size 8 | 9 | def get_upgrade_content_size(self): 10 | return self._total 11 | 12 | def work(self): 13 | current = 0 14 | step = 200 15 | while current < self._total: 16 | if self._is_stopped: 17 | break 18 | 19 | if current > 2000: 20 | self.emit('error', self._key, 'upgrade failed') 21 | current += step 22 | self.emit('progress', self._key, current, self._total) 23 | 24 | if current >= step: 25 | self.emit('finish', self._key) 26 | 27 | def stop(self): 28 | self._is_stopped = True 29 | -------------------------------------------------------------------------------- /tests/mocker/upgrade_workers/normal_worker.py: -------------------------------------------------------------------------------- 1 | from aceinna.devices.base import UpgradeWorkerBase 2 | from aceinna.devices.upgrade_workers import UPGRADE_GROUP 3 | 4 | 5 | class NormalWorker(UpgradeWorkerBase): 6 | def __init__(self): 7 | super(NormalWorker, self).__init__() 8 | self._total = 1024*1024 # 1M size 9 | self._group = UPGRADE_GROUP.FIRMWARE 10 | 11 | def get_upgrade_content_size(self): 12 | return self._total 13 | 14 | def work(self): 15 | current = 0 16 | step = 1000 17 | while current < self._total: 18 | if self._is_stopped: 19 | break 20 | current += step 21 | self.emit('progress', self._key, current, self._total) 22 | 23 | if current >= step: 24 | self.emit('finish', self._key) 25 | 26 | def stop(self): 27 | self._is_stopped = True 28 | -------------------------------------------------------------------------------- /tests/test_ans_devices/StringStream.py: -------------------------------------------------------------------------------- 1 | class StringStream: 2 | """A simple class to hold text so that when passed 3 | between functions, the object is passed by reference 4 | and memory does not need to be repeatedly allocated for the string. 5 | 6 | This class was written here to avoid adding a dependency 7 | to the project. 8 | """ 9 | 10 | def __init__(self, raw_text, debug=False): 11 | self.raw_text = raw_text 12 | self.index = 0 13 | self.len = len(raw_text) 14 | 15 | def read(self, count): 16 | """Read count characters starting at self.index, 17 | and return those characters as a string 18 | """ 19 | new_index = self.index + count 20 | if new_index > self.len: 21 | buf = self.raw_text[self.index :] # return to the end, don't fail 22 | else: 23 | buf = self.raw_text[self.index : new_index] 24 | self.index = new_index 25 | 26 | return buf 27 | 28 | def seek(self, offset): 29 | """Advance the index of this StringStream by offset characters""" 30 | self.index = self.index + offset 31 | 32 | def advance_past_chars(self, chars): 33 | """Advance the index past specific chars 34 | Args chars (list): list of characters to advance past 35 | 36 | Return substring that was advanced past 37 | """ 38 | start_index = self.index 39 | while True: 40 | current_char = self.raw_text[self.index] 41 | self.index += 1 42 | if current_char in chars: 43 | break 44 | 45 | elif self.index == self.len: 46 | break 47 | 48 | return self.raw_text[start_index : self.index - 1] 49 | 50 | def advance_past_string_with_gdb_escapes(self, chars_to_remove_gdb_escape=None): 51 | """characters that gdb escapes that should not be 52 | escaped by this parser 53 | """ 54 | 55 | if chars_to_remove_gdb_escape is None: 56 | chars_to_remove_gdb_escape = ['"'] 57 | 58 | buf = "" 59 | while True: 60 | c = self.raw_text[self.index] 61 | self.index += 1 62 | 63 | if c == "\\": 64 | # We are on a backslash and there is another character after the backslash 65 | # to parse. Handle this case specially since gdb escaped it for us 66 | 67 | # Get the next char that is being escaped 68 | c2 = self.raw_text[self.index] 69 | self.index += 1 70 | # only store the escaped character in the buffer; don't store the backslash 71 | # (don't leave it escaped) 72 | buf += c2 73 | 74 | elif c == '"': 75 | # Quote is closed. Exit (and don't include the end quote). 76 | break 77 | 78 | else: 79 | # capture this character, and keep capturing 80 | buf += c 81 | return buf 82 | -------------------------------------------------------------------------------- /tests/test_ans_devices/constants.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | DEFAULT_PROCESS_TIMEOUT_SEC = 1 4 | DEFAULT_TIME_TO_CHECK_FOR_ADDITIONAL_OUTPUT_SEC = 0.2 5 | USING_WINDOWS = os.name == "nt" 6 | 7 | 8 | class ProcessTimeoutError(ValueError): 9 | """Raised when no response is recieved from python driver after the timeout has been triggered""" 10 | 11 | pass 12 | -------------------------------------------------------------------------------- /tests/test_ans_devices/printcolor.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | USING_WINDOWS = os.name == "nt" 4 | 5 | 6 | def print_red(x): 7 | if USING_WINDOWS: 8 | print(x) 9 | else: 10 | print("\033[91m {}\033[00m".format(x)) 11 | 12 | 13 | def print_green(x): 14 | if USING_WINDOWS: 15 | print(x) 16 | else: 17 | print("\033[92m {}\033[00m".format(x)) 18 | 19 | 20 | def print_cyan(x): 21 | if USING_WINDOWS: 22 | print(x) 23 | else: 24 | print("\033[96m {}\033[00m".format(x)) 25 | 26 | 27 | def fmt_green(x): 28 | if USING_WINDOWS: 29 | return x 30 | else: 31 | return "\033[92m {}\033[00m".format(x) 32 | 33 | 34 | def fmt_cyan(x): 35 | if USING_WINDOWS: 36 | return x 37 | else: 38 | return "\033[96m {}\033[00m".format(x) 39 | -------------------------------------------------------------------------------- /tests/test_build_message.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | try: 4 | from aceinna.framework.utils import helper 5 | from aceinna.devices.dmu import dmu_helper 6 | except: # pylint: disable=bare-except 7 | sys.path.append('./src') 8 | from aceinna.framework.utils import helper 9 | from aceinna.devices.dmu import dmu_helper 10 | 11 | 12 | class TestOpenPacketMessage(unittest.TestCase): 13 | ''' 14 | Test Open Packet Message 15 | 1. build_packet 16 | 2. build_input_packet 17 | ''' 18 | 19 | def test_build_packet(self): 20 | # gA 21 | msg = helper.build_packet('gA') 22 | expect_msg = [85, 85, 103, 65, 0, 49, 10] 23 | self.assertEqual(msg, expect_msg, 'gA Message is matched') 24 | 25 | # pG 26 | msg = helper.build_packet('pG') 27 | expect_msg = [85, 85, 112, 71, 0, 93, 95] 28 | self.assertEqual(msg, expect_msg, 'pG Message is matched') 29 | 30 | def test_build_packet_with_params(self): 31 | # uP 32 | param_index = [2, 0, 0, 0] 33 | int64_value = [10, 0, 0, 0, 0, 0, 0, 0] 34 | msg = helper.build_packet('uP', param_index + int64_value) 35 | expect_msg = [85, 85, 117, 80, 12] + param_index+int64_value + [127, 28] 36 | self.assertEqual(msg, expect_msg, 'uP Message is matched') 37 | 38 | # gP 39 | param_index = [2, 0, 0, 0] 40 | msg = helper.build_packet('gP', param_index) 41 | expect_msg = [85, 85, 103, 80, 4] + param_index + [166, 214] 42 | self.assertEqual(msg, expect_msg, 'gP Message is matched') 43 | 44 | 45 | class TestDMUPacketMessage(unittest.TestCase): 46 | ''' 47 | Test DMU Packet Message 48 | 1. build_packet 49 | ''' 50 | 51 | def test_build_packet(self): 52 | # RF 53 | read_fields = [0, 1] + [0, 2] 54 | msg = dmu_helper.build_packet('RF', read_fields) 55 | len_of_payload = len(read_fields) 56 | number_of_fields = int(len_of_payload/2) 57 | expect_msg = [85, 85, 82, 70] \ 58 | + [len_of_payload+1] + [number_of_fields] \ 59 | + read_fields+[201, 194] 60 | self.assertEqual(msg, expect_msg, 'RF Message is matched') 61 | 62 | # WF 63 | write_fields = [0, 1, 0, 1] + [0, 2, 0, 2] 64 | msg = dmu_helper.build_packet('WF', write_fields) 65 | len_of_payload = len(write_fields) 66 | number_of_fields = int(len(write_fields)/4) 67 | expect_msg = [85, 85, 87, 70] \ 68 | + [len_of_payload+1] + [number_of_fields]\ 69 | + write_fields + [65, 107] 70 | self.assertEqual(msg, expect_msg, 'WF Message is matched') 71 | 72 | # Other 73 | payload = [0, 1, 0, 2] 74 | msg = dmu_helper.build_packet('TS', payload) 75 | expect_msg = [85, 85, 84, 83] + [len(payload)]+payload + [19, 35] 76 | self.assertEqual(msg, expect_msg, 'TS Message is matched') 77 | 78 | 79 | if __name__ == '__main__': 80 | unittest.main() 81 | -------------------------------------------------------------------------------- /tests/test_communicator.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | import threading 4 | import time 5 | from websocket import create_connection 6 | 7 | try: 8 | from aceinna.framework.communicators import SerialPort 9 | except: # pylint: disable=bare-except 10 | sys.path.append('./src') 11 | from aceinna.framework.communicators import SerialPort 12 | 13 | 14 | # pylint: disable=missing-class-docstring 15 | @unittest.skip 16 | class TestUARTCommunicator(unittest.TestCase): 17 | def setUp(self): 18 | pass 19 | 20 | def tearDown(self): 21 | pass 22 | 23 | def test_cancel_while_find(self): 24 | communicator = SerialPort() 25 | 26 | def do_find_device(): 27 | communicator.find_device(lambda provider: {}) 28 | 29 | thread = threading.Thread( 30 | target=do_find_device, args=()) 31 | thread.start() 32 | 33 | time.sleep(1) 34 | communicator.close() 35 | self.assertTrue(True, 'Find device') 36 | 37 | 38 | if __name__ == '__main__': 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | 4 | try: 5 | from aceinna.framework.configuration import ( 6 | DEV_CONFIG, PROD_CONFIG, get_config) 7 | except: 8 | sys.path.append('./src') 9 | from aceinna.framework.configuration import ( 10 | DEV_CONFIG, PROD_CONFIG, get_config) 11 | 12 | 13 | class TestConfig(unittest.TestCase): 14 | def test_get_dev_config(self): 15 | setattr(sys, '__dev__', True) 16 | config = get_config() 17 | self.assertEqual(config, DEV_CONFIG) 18 | 19 | def test_get_prod_config(self): 20 | delattr(sys, '__dev__') 21 | config = get_config() 22 | self.assertEqual(config, PROD_CONFIG) 23 | 24 | def test_get_prod_config_if_disable_dev(self): 25 | setattr(sys, '__dev__', False) 26 | config = get_config() 27 | self.assertEqual(config, PROD_CONFIG) 28 | 29 | 30 | if __name__ == '__main__': 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /tests/test_device_manager.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | 4 | try: 5 | from aceinna.devices import DeviceManager 6 | from aceinna.devices.base.provider_base import OpenDeviceBase 7 | from aceinna.devices.openimu.uart_provider import Provider as OpenIMUUartProvider 8 | from aceinna.devices.openrtk.uart_provider import Provider as OpenRTKUartProvider 9 | from aceinna.devices.rtkl.uart_provider import Provider as RTKLUartProvider 10 | from aceinna.devices.dmu.uart_provider import Provider as OpenDMUUartProvider 11 | from mocker.communicator import MockCommunicator 12 | except: # pylint: disable=bare-except 13 | sys.path.append('./src') 14 | sys.path.append('./tests') 15 | from aceinna.devices import DeviceManager 16 | from aceinna.devices.base.provider_base import OpenDeviceBase 17 | from aceinna.devices.openimu.uart_provider import Provider as OpenIMUUartProvider 18 | from aceinna.devices.openrtk.uart_provider import Provider as OpenRTKUartProvider 19 | from aceinna.devices.rtkl.uart_provider import Provider as RTKLUartProvider 20 | from aceinna.devices.dmu.uart_provider import Provider as OpenDMUUartProvider 21 | from mocker.communicator import MockCommunicator 22 | 23 | 24 | def build_provider(device_access_type='IMU', filter_device_type=None): 25 | mocker_communicator_options = {'device': device_access_type} 26 | communicator = MockCommunicator(mocker_communicator_options) 27 | provider = DeviceManager.ping( 28 | communicator, communicator.device_access, filter_device_type) 29 | communicator.close() 30 | return provider 31 | 32 | # pylint: disable=missing-class-docstring 33 | 34 | 35 | class TestDeviceManager(unittest.TestCase): 36 | def test_ping_openimu_with_default(self): 37 | provider = build_provider('IMU') 38 | self.assertTrue(isinstance(provider, OpenIMUUartProvider), 39 | 'OpenIMU UART Provider') 40 | 41 | def test_ping_openimu_with_specified_device_type(self): 42 | provider = build_provider('IMU', 'IMU') 43 | self.assertTrue(isinstance(provider, OpenIMUUartProvider), 44 | 'OpenIMU UART Provider') 45 | 46 | def test_ping_openrtk_with_default(self): 47 | provider = build_provider('RTK') 48 | self.assertTrue(isinstance(provider, OpenRTKUartProvider), 49 | 'OpenRTK UART Provider') 50 | 51 | def test_ping_openrtk_with_specified_device_type(self): 52 | provider = build_provider('RTK', 'RTK') 53 | self.assertTrue(isinstance(provider, OpenRTKUartProvider), 54 | 'OpenRTK UART Provider') 55 | 56 | def test_ping_dmu_with_default(self): 57 | provider = build_provider('DMU') 58 | self.assertTrue(isinstance(provider, OpenDMUUartProvider), 59 | 'DMU UART Provider') 60 | 61 | def test_ping_dmu_with_specified_device_type(self): 62 | provider = build_provider('DMU', 'DMU') 63 | self.assertTrue(isinstance(provider, OpenDMUUartProvider), 64 | 'DMU UART Provider') 65 | 66 | def test_ping_rtkl_with_specified_device_type(self): 67 | provider = build_provider('RTKL', 'RTKL') 68 | self.assertTrue(isinstance(provider, RTKLUartProvider), 69 | 'RTKL UART Provider') 70 | 71 | def test_ping_device_with_unmatched_device_type(self): 72 | provider = build_provider('IMU', 'RTK') 73 | self.assertTrue(provider is None, 'OpenIMU UART Provider') 74 | 75 | def test_ping_same_device_with_samed_provider_instance(self): 76 | communicator = MockCommunicator({'device': 'IMU'}) 77 | provider1 = DeviceManager.ping( 78 | communicator, communicator.device_access, None) 79 | provider2 = DeviceManager.ping( 80 | communicator, communicator.device_access, None) 81 | communicator.close() 82 | self.assertEqual(provider1, provider2) 83 | 84 | # TODO: missing commuinicator with LAN 85 | 86 | 87 | if __name__ == '__main__': 88 | unittest.main() 89 | -------------------------------------------------------------------------------- /tests/test_event_base.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from src.aceinna.devices.base import EventBase 3 | 4 | 5 | class TestEventBase(unittest.TestCase): 6 | def setUp(self): 7 | self.reset_event_trigger_flag() 8 | 9 | def reset_event_trigger_flag(self): 10 | self.event_triggered = 0 11 | 12 | def event_callback(self, *args): 13 | self.event_triggered = self.event_triggered+1 14 | 15 | def test_single_event(self): 16 | event_base = EventBase() 17 | event_base.on('event1', self.event_callback) 18 | event_base.emit('event1', 'event1') 19 | self.assertTrue(self.event_triggered == 1, 'Event triggered') 20 | 21 | def test_multi_event(self): 22 | event_base = EventBase() 23 | event_base.on('event1', self.event_callback) 24 | event_base.on('event2', self.event_callback) 25 | event_base.emit('event1', 'event1') 26 | event_base.emit('event2', 'event2') 27 | 28 | self.assertTrue(self.event_triggered == 2, 'Event triggered') 29 | 30 | def test_duplicate_bind_event(self): 31 | event_base = EventBase() 32 | event_base.on('event1', self.event_callback) 33 | event_base.on('event1', self.event_callback) 34 | 35 | event_base.emit('event1', 'event1') 36 | 37 | self.assertTrue(self.event_triggered == 2, 'Event triggered') 38 | 39 | def test_non_bind_event(self): 40 | event_base = EventBase() 41 | event_base.on('event1', self.event_callback) 42 | 43 | event_base.emit('event2', 'event1') 44 | 45 | self.assertTrue(self.event_triggered == 0, 'Event triggered') 46 | -------------------------------------------------------------------------------- /tests/test_loader.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | 4 | try: 5 | from aceinna.bootstrap.loader import (Loader, DefaultApp, CommandLineApp) 6 | except: 7 | sys.path.append('./src') 8 | from aceinna.bootstrap.loader import (Loader, DefaultApp, CommandLineApp) 9 | 10 | 11 | class TesLoader(unittest.TestCase): 12 | 13 | def test_create_webserver(self): 14 | instance = Loader.create('default', dict()) 15 | self.assertTrue(isinstance(instance, DefaultApp)) 16 | 17 | def test_create_cli(self): 18 | instance = Loader.create('cli', dict()) 19 | self.assertTrue(isinstance(instance, CommandLineApp)) 20 | 21 | def test_create_other(self): 22 | self.assertRaises(ValueError, Loader.create, 'other', dict()) 23 | 24 | 25 | if __name__ == '__main__': 26 | unittest.main() 27 | -------------------------------------------------------------------------------- /tests/test_mag_align.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | try: 5 | raise Exception('use local code') 6 | except: 7 | sys.path.append('./src') 8 | from aceinna.framework.utils import helper 9 | from aceinna.devices.decorator import with_device_message 10 | from aceinna.tools import Detector 11 | 12 | global MESSAGE_CENTER 13 | global PROERTIES 14 | 15 | 16 | def on_find_device(device): 17 | ''' 18 | callback after find device 19 | ''' 20 | global MESSAGE_CENTER 21 | global PROERTIES 22 | device.setup(None) 23 | 24 | PROERTIES = device.properties 25 | MESSAGE_CENTER = device._message_center 26 | 27 | #send_ping_command() 28 | 29 | print('1. Request status (command 0)') 30 | send_mag_status_command() 31 | time.sleep(1) 32 | 33 | print('2. Start auto alignment (command 1)') 34 | send_mag_start_command() 35 | time.sleep(1) 36 | 37 | print('3. Request status') 38 | send_mag_status_command() 39 | time.sleep(1) 40 | 41 | print('4. Rotate device around it’s Z-axis until automatic response send back n=by unit (message “CD” or “CB”)') 42 | while True: 43 | user_input = input() 44 | if user_input == 'ok': 45 | mag_status_result = send_mag_status_command() 46 | 47 | if mag_status_result['data'] == [0]: 48 | print('5. Request status (command 0) – status 0 returned \n') 49 | break 50 | else: 51 | print('5. Request status (command 0) – status {0} returned, continue rotate \n'.format( 52 | mag_status_result['data'])) 53 | 54 | time.sleep(1) 55 | 56 | print('6. quest values (command 7) and check them') 57 | send_mag_request_values_command() 58 | time.sleep(1) 59 | 60 | print('7.Accept/save values using command 5.') 61 | send_mag_save_values_command() 62 | 63 | print('---- Going to sleep 5s, to wait the Algorithm reset ----\n') 64 | time.sleep(3) 65 | 66 | print('8. Send pG') 67 | send_ping_command() 68 | time.sleep(1) 69 | 70 | print('9. Send gA') 71 | send_get_all_parameters_command() 72 | time.sleep(1) 73 | 74 | device.close() 75 | 76 | 77 | @with_device_message 78 | def send_ping_command(): 79 | command_line = helper.build_input_packet('pG') 80 | result = yield MESSAGE_CENTER.build(command=command_line, timeout=3) 81 | print('pG result: {0}\n'.format(result['data'])) 82 | return result 83 | 84 | 85 | @with_device_message 86 | def send_get_all_parameters_command(): 87 | command_line = helper.build_input_packet('gA') 88 | result = yield MESSAGE_CENTER.build(command=command_line) 89 | print('gA result: {0} \n'.format(result['data'])) 90 | return result 91 | 92 | 93 | @with_device_message 94 | def send_mag_status_command(): 95 | command_line = helper.build_input_packet( 96 | 'ma', PROERTIES, 'status') # 0 97 | result = yield MESSAGE_CENTER.build(command=command_line) 98 | print('ma status result: {0} \n'.format(result['data'])) 99 | return result 100 | 101 | 102 | @with_device_message 103 | def send_mag_start_command(): 104 | command_line = helper.build_input_packet( 105 | 'ma', PROERTIES, 'start') # 1 106 | result = yield MESSAGE_CENTER.build(command=command_line) 107 | print('ma start result: {0} \n'.format(result['data'])) 108 | return result 109 | 110 | 111 | @with_device_message 112 | def send_mag_request_values_command(): 113 | command_line = helper.build_input_packet( 114 | 'ma', PROERTIES, 'stored') # 7 115 | result = yield MESSAGE_CENTER.build(command=command_line) 116 | print('ma request values result: {0} \n'.format(result['data'])) 117 | return result 118 | 119 | 120 | @with_device_message 121 | def send_mag_save_values_command(): 122 | command_line = helper.build_input_packet('ma', PROERTIES, 'save') # 5 123 | result = yield MESSAGE_CENTER.build(command=command_line) 124 | print('ma save values result: {0} \n'.format(result['data'])) 125 | return result 126 | 127 | 128 | def simple_start(): 129 | detector = Detector() 130 | detector.find(on_find_device) 131 | 132 | 133 | if __name__ == '__main__': 134 | simple_start() 135 | -------------------------------------------------------------------------------- /tests/test_models.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | try: 4 | from aceinna.models.args import (WebserverArgs,DetectorArgs) 5 | except: # pylint: disable=bare-except 6 | print('load package from local') 7 | sys.path.append('./src') 8 | from aceinna.models.args import (WebserverArgs,DetectorArgs) 9 | 10 | 11 | def test_web_args(): 12 | define_port = 8001 13 | web_args = WebserverArgs(port=define_port) 14 | assert web_args.port == define_port 15 | 16 | 17 | def test_web_args_with_default(): 18 | ''' 19 | 'interface': 'uart', 20 | 'device_type': 'auto', 21 | 'port': 'auto', 22 | 'baudrate': 'auto', 23 | 'com_port': 'auto', 24 | 'debug': False, 25 | 'with_data_log': False, 26 | 'console_log': False, 27 | 'set_user_para': False, 28 | 'ntrip_client': False 29 | ''' 30 | web_args = WebserverArgs() 31 | assert web_args is not None 32 | assert web_args.interface == 'uart' 33 | assert web_args.port == 'auto' 34 | assert web_args.baudrate == 'auto' 35 | assert web_args.com_port == 'auto' 36 | assert web_args.debug == False 37 | assert web_args.with_data_log == False 38 | assert web_args.console_log == False 39 | assert web_args.set_user_para == False 40 | assert web_args.ntrip_client == False 41 | 42 | def test_detect_with_default(): 43 | ''' 44 | 'device_type': 'auto', 45 | 'baudrate': 'auto', 46 | 'com_port': 'auto' 47 | ''' 48 | detect_args = DetectorArgs() 49 | assert detect_args.device_type == 'auto' 50 | assert detect_args.com_port == 'auto' 51 | assert detect_args.baudrate == 'auto' 52 | 53 | 54 | 55 | if __name__ == '__main__': 56 | test_web_args() 57 | test_web_args_with_default() 58 | test_detect_with_default() 59 | -------------------------------------------------------------------------------- /tests/test_nmea_parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | import traceback 3 | import time 4 | import cProfile 5 | 6 | GPZDA_DATA_LEN = 39 7 | 8 | def nmea_checksum(data): 9 | nmea_str = data[1:len(data) - 2] 10 | nmeadata = nmea_str[0:len(nmea_str)-3] 11 | cksum = nmea_str[len(nmea_str)-2:len(nmea_str)] 12 | 13 | calc_cksum = 0 14 | for s in nmeadata: 15 | calc_cksum ^= ord(s) 16 | return int(cksum, 16), calc_cksum 17 | 18 | def nmea_checksum_v1(data): 19 | data = data.replace("\r", "").replace("\n", "").replace("$", "") 20 | nmeadata, cksum = re.split('\*', data) 21 | calc_cksum = 0 22 | for s in nmeadata: 23 | calc_cksum ^= ord(s) 24 | return int(cksum, 16), calc_cksum 25 | 26 | def parse_nmea_v1(raw_data): 27 | print('v1') 28 | print('start', time.time()) 29 | 30 | nmea_buffer = [] 31 | nmea_sync = 0 32 | 33 | if raw_data[0] != 0x24: 34 | return 35 | 36 | for bytedata in raw_data: 37 | if bytedata == 0x24: 38 | nmea_buffer = [] 39 | nmea_sync = 0 40 | nmea_buffer.append(bytedata) 41 | else: 42 | nmea_buffer.append(bytedata) 43 | if nmea_sync == 0: 44 | if bytedata == 0x0D: 45 | nmea_sync = 1 46 | elif nmea_sync == 1: 47 | if bytedata == 0x0A: 48 | try: 49 | str_nmea = bytes(nmea_buffer).decode() 50 | cksum, calc_cksum = nmea_checksum(str_nmea) 51 | if cksum == calc_cksum: 52 | if str_nmea.find("$GPGGA") != -1: 53 | pass 54 | except Exception as e: 55 | pass 56 | nmea_buffer = [] 57 | nmea_sync = 0 58 | 59 | print('end ', time.time()) 60 | 61 | def parse_nmea_v2(raw_data): 62 | print('v2') 63 | print('start', time.time()) 64 | if data[0] != 0x24 or data[1] != 0x47 or data[2] != 0x50: 65 | return 66 | 67 | temp_str_nmea = data.decode('utf-8') 68 | if (temp_str_nmea.find("\r\n", len(temp_str_nmea)-2, len(temp_str_nmea)) != -1): 69 | str_nmea = temp_str_nmea 70 | elif(temp_str_nmea.find("\r\n", GPZDA_DATA_LEN-2, GPZDA_DATA_LEN) != -1): 71 | str_nmea = temp_str_nmea[0:GPZDA_DATA_LEN] 72 | else: 73 | return 74 | 75 | try: 76 | cksum, calc_cksum = nmea_checksum(str_nmea) 77 | if cksum == calc_cksum: 78 | if str_nmea.find("$GPGGA", 0, 6) != -1: 79 | pass 80 | except Exception as e: 81 | print('NMEA fault:{0}'.format(e)) 82 | print('end ', time.time()) 83 | 84 | if __name__ == '__main__': 85 | data = '$GPGGA,042547.00,3129.6667218,N,12021.7694487,E,4,24,0.6,119.227,M,0.000,M,2.0,*7f\r\n'.encode() 86 | cProfile.run("parse_nmea_v1(data)") 87 | cProfile.run("parse_nmea_v2(data)") 88 | #parse_nmea_v1(data) 89 | #parse_nmea_v2(data) 90 | 91 | 92 | -------------------------------------------------------------------------------- /tests/test_ntrip_client.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import json 4 | import unittest 5 | import threading 6 | import time 7 | 8 | try: 9 | from aceinna.devices.widgets import NTRIPClient 10 | except: 11 | sys.path.append('./src') 12 | from aceinna.devices.widgets import NTRIPClient 13 | 14 | local_config_file_path = os.path.join( 15 | os.getcwd(), 'setting', 'RTK330L', 'RTK_INS', 'RTK330L.json') 16 | properties = None 17 | 18 | if os.path.isfile(local_config_file_path): 19 | with open(local_config_file_path) as json_data: 20 | properties = json.load(json_data) 21 | 22 | 23 | def handle_data_parsed(data): 24 | pass 25 | 26 | 27 | class TestNtripClient(unittest.TestCase): 28 | _ntrip_client = None 29 | 30 | def _start_ntrip_client(self): 31 | self._ntrip_client = NTRIPClient(properties) 32 | self._ntrip_client.on('parsed', handle_data_parsed) 33 | self._ntrip_client.run() 34 | 35 | def test_connect_with_ntrip_server(self): 36 | threading.Thread(target=self._start_ntrip_client).start() 37 | time.sleep(3) 38 | is_connected = self._ntrip_client.is_connected 39 | self._ntrip_client.close() 40 | 41 | self.assertEqual(self._ntrip_client.is_connected, 42 | 0, 'Ntrip Server connected') 43 | 44 | 45 | if __name__ == '__main__': 46 | unittest.main() 47 | -------------------------------------------------------------------------------- /tests/test_parse_eth_100base_t1_buffer.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | try: 4 | from aceinna.framework.utils import helper 5 | except: 6 | sys.path.append('./src') 7 | from aceinna.framework.utils import helper 8 | from aceinna.devices.parsers.ins401_message_parser import EthernetMessageParser 9 | 10 | 11 | def parse_with_helper(): 12 | bin_file = '/Users/songyiwei/projects/python-openimu/data/another/user_2021_07_26_10_30_41.bin' 13 | 14 | with open(bin_file, 'rb') as buf_r: 15 | tmp_data = buf_r.read(1000) 16 | 17 | if tmp_data: 18 | response = helper._parse_eth_100base_t1_buffer(tmp_data) 19 | print(response) 20 | 21 | 22 | def parse_with_parser(): 23 | 24 | def continuous_message_handler(packet_type, data, event_time, raw): 25 | print(packet_type, event_time) 26 | 27 | parser = EthernetMessageParser(None) 28 | #parser.on('continuous_message', continuous_message_handler) 29 | 30 | bin_file = '/Users/songyiwei/projects/python-openimu/data/20210729/user_2021_07_29_15_50_53.bin' 31 | #bin_file = '/Users/songyiwei/projects/python-openimu/data/another/user_2021_07_26_10_30_41.bin' 32 | 33 | print(time.time()) 34 | read_size = 1024*1024 35 | count = 0 36 | with open(bin_file, 'rb') as buf_r: 37 | tmp_data = buf_r.read(read_size) 38 | 39 | if len(tmp_data) > 0: 40 | parser.analyse(tmp_data) 41 | # tmp_data = buf_r.read(read_size) 42 | # if tmp_data: 43 | print(time.time()) 44 | 45 | 46 | if __name__ == '__main__': 47 | parse_with_parser() 48 | -------------------------------------------------------------------------------- /tests/test_ping.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | 4 | try: 5 | from aceinna.devices.openimu.uart_provider import Provider as OpenIMUUartProvider 6 | # from aceinna.devices.openrtk.uart_provider import Provider as OpenRTKUartProvider 7 | # from aceinna.devices.openrtk.lan_provider import Provider as OpenRTKLANProvider 8 | # from aceinna.devices.rtkl.uart_provider import Provider as OpenRTKLUartProvider 9 | except: 10 | sys.path.append('./src') 11 | from aceinna.devices.openimu.uart_provider import Provider as OpenIMUUartProvider 12 | # from aceinna.devices.openrtk.uart_provider import Provider as OpenRTKUartProvider 13 | # from aceinna.devices.openrtk.lan_provider import Provider as OpenRTKLANProvider 14 | # from aceinna.devices.rtkl.uart_provider import Provider as OpenRTKLUartProvider 15 | 16 | 17 | PRODUCT_NORMAL = 'OpenIMU330BI' 18 | PRODUCT_OPTIONAL = '{0} EVB'.format(PRODUCT_NORMAL) 19 | PART_NUM = '5020-1800-01' 20 | SERIAL_NUM = 'SN:2074001166' 21 | APP_NAME = 'IMU' 22 | APP_VERSION = '02.01.51' 23 | 24 | DEVICE_NORMAL = '{0} {1} {2} {3}'.format( 25 | PRODUCT_NORMAL, PART_NUM, APP_VERSION, SERIAL_NUM) 26 | DEVICE_OPTIONAL = '{0} {1} {2} {3}'.format( 27 | PRODUCT_OPTIONAL, PART_NUM, APP_VERSION, SERIAL_NUM) 28 | 29 | VERSION_NORMAL = '{0} {1} {2}'.format(PRODUCT_NORMAL, APP_NAME, APP_VERSION) 30 | VERSION_SHORT = '{0} {1}'.format(APP_NAME, APP_VERSION) 31 | 32 | DEVICE_BOOTLOADER = 'Bootloader Unit SN:xxxxxx' 33 | VERSION_BOOTLOADER = '' 34 | 35 | INVALID_DEVICE = 'xx xx' 36 | 37 | 38 | class TestDevicePing(unittest.TestCase): 39 | def test_ping_openimu_normal(self): 40 | provider = OpenIMUUartProvider(None) 41 | provider.bind_device_info(None, DEVICE_NORMAL, VERSION_NORMAL) 42 | 43 | self.assertEqual( 44 | [provider.device_info['name'], provider.device_info['product_name'], 45 | provider.app_info['app_name'], provider.app_info['product_name']], 46 | [PRODUCT_NORMAL, PRODUCT_NORMAL, APP_NAME, PRODUCT_NORMAL] 47 | ) 48 | 49 | def test_ping_openimu_device_name_optional(self): 50 | provider = OpenIMUUartProvider(None) 51 | provider.bind_device_info(None, DEVICE_OPTIONAL, VERSION_NORMAL) 52 | 53 | self.assertEqual( 54 | [provider.device_info['name'], provider.device_info['product_name'], 55 | provider.app_info['app_name'], provider.app_info['product_name']], 56 | [PRODUCT_OPTIONAL, PRODUCT_NORMAL, APP_NAME, PRODUCT_NORMAL] 57 | ) 58 | 59 | def test_ping_openimu_normal_short_app_version(self): 60 | provider = OpenIMUUartProvider(None) 61 | provider.bind_device_info(None, PRODUCT_NORMAL, VERSION_SHORT) 62 | 63 | self.assertEqual( 64 | [provider.device_info['name'], provider.device_info['product_name'], 65 | provider.app_info['app_name'], provider.app_info['product_name']], 66 | [PRODUCT_NORMAL, PRODUCT_NORMAL, APP_NAME, ''] 67 | ) 68 | 69 | def test_ping_openimu_device_name_optional_short_app_version(self): 70 | provider = OpenIMUUartProvider(None) 71 | provider.bind_device_info(None, DEVICE_OPTIONAL, VERSION_SHORT) 72 | 73 | self.assertEqual( 74 | [provider.device_info['name'], provider.device_info['product_name'], 75 | provider.app_info['app_name'], provider.app_info['product_name']], 76 | [PRODUCT_OPTIONAL, PRODUCT_NORMAL, APP_NAME, ''] 77 | ) 78 | 79 | def test_ping_openimu_bootloader(self): 80 | provider = OpenIMUUartProvider(None) 81 | provider.bind_device_info(None, DEVICE_BOOTLOADER, VERSION_BOOTLOADER) 82 | 83 | self.assertEqual( 84 | [provider.device_info['name'], provider.device_info['product_name'], 85 | provider.app_info['app_name'], provider.app_info['product_name']], 86 | ['Bootloader', 'Bootloader', 'IMU', ''] 87 | ) 88 | 89 | def test_ping_openimu_invalid_device(self): 90 | provider = OpenIMUUartProvider(None) 91 | provider.bind_device_info(None, INVALID_DEVICE, '') 92 | 93 | self.assertEqual( 94 | [provider.device_info['name'], provider.device_info['product_name'], 95 | provider.app_info['app_name'], provider.app_info['product_name']], 96 | ['xx', 'xx', 'IMU', ''] 97 | ) 98 | 99 | 100 | if __name__ == '__main__': 101 | unittest.main() 102 | -------------------------------------------------------------------------------- /tests/test_rtcm_parser.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | #import unittest 4 | 5 | try: 6 | from aceinna.core.gnss import RTCMParser 7 | # from aceinna.devices.openrtk.uart_provider import Provider as OpenRTKUartProvider 8 | # from aceinna.devices.openrtk.lan_provider import Provider as OpenRTKLANProvider 9 | # from aceinna.devices.rtkl.uart_provider import Provider as OpenRTKLUartProvider 10 | except: 11 | sys.path.append('./src') 12 | from aceinna.core.gnss import RTCMParser 13 | 14 | global parsed_packet_count 15 | parsed_packet_count = 0 16 | 17 | 18 | def handle_parsed_data(data): 19 | global parsed_packet_count 20 | parsed_packet_count += len(data) 21 | 22 | 23 | rtcm_parser = RTCMParser() 24 | rtcm_parser.on('parsed', handle_parsed_data) 25 | 26 | rtcm_file_path = '/Users/songyiwei/projects/runtime-log/app/server/data/OpenRTK330L-1975000205/1624346077621/rtcm_rover.bin' #os.path.join(os.getcwd(), 'data', 'collect', 'rtcm_base_2021_05_29_09_23_54.bin') 27 | 28 | with open(rtcm_file_path, 'rb') as buf_r: 29 | s = 0 30 | while True: 31 | tmp_data = buf_r.read(1024) 32 | if tmp_data: 33 | s += 1 34 | rtcm_parser.receive(tmp_data) 35 | else: 36 | break 37 | print('Found header count:{0}, Parsed parcket count:{1}, CRC passed count:{2}, CRC failed count:{3}'.format( 38 | rtcm_parser.found_header_count, 39 | parsed_packet_count, 40 | rtcm_parser.crc_passed_count, 41 | rtcm_parser.crc_failed_count 42 | )) 43 | print('All is read, read times:', s) 44 | -------------------------------------------------------------------------------- /tests/test_throttle.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | try: 4 | from aceinna.framework.decorator import throttle 5 | except: 6 | sys.path.append('./src') 7 | from aceinna.framework.decorator import throttle 8 | 9 | 10 | @throttle(seconds=0.01) 11 | def greeting(): 12 | print('hello world', time.time()) 13 | 14 | 15 | if __name__ == '__main__': 16 | for i in range(100): 17 | greeting() 18 | time.sleep(0.001) 19 | -------------------------------------------------------------------------------- /tests/test_upgrade_center.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | import time 4 | 5 | try: 6 | from mocker.upgrade_workers.normal_worker import NormalWorker 7 | from mocker.upgrade_workers.error_worker import ErrorWorker 8 | from aceinna.devices.upgrade_center import UpgradeCenter 9 | from aceinna.devices.upgrade_workers import UPGRADE_EVENT, UPGRADE_GROUP 10 | except: 11 | sys.path.append('./src') 12 | sys.path.append('./tests') 13 | from mocker.upgrade_workers.normal_worker import NormalWorker 14 | from mocker.upgrade_workers.error_worker import ErrorWorker 15 | from aceinna.devices.upgrade_center import UpgradeCenter 16 | from aceinna.devices.upgrade_workers import UPGRADE_EVENT, UPGRADE_GROUP 17 | 18 | 19 | class TestUpgradeCenter(unittest.TestCase): 20 | def handle_done(self): 21 | self.assertTrue(True, 'all works done') 22 | 23 | def handle_progress(self, current, total): 24 | self.assertTrue(True, 'all works done') 25 | 26 | def handle_error(self, message): 27 | self.assertTrue(True, 'Encounter error') 28 | 29 | def test_one_worker(self): 30 | upgrade_center = UpgradeCenter() 31 | upgrade_center.register(NormalWorker()) 32 | upgrade_center.on('finish', self.handle_done) 33 | upgrade_center.start() 34 | 35 | def test_start_2_times(self): 36 | upgrade_center = UpgradeCenter() 37 | upgrade_center.register(NormalWorker()) 38 | _ = upgrade_center.start() 39 | second_result = upgrade_center.start() 40 | self.assertFalse(second_result, 'Cannot start 2 times') 41 | 42 | def test_normal_stop(self): 43 | upgrade_center = UpgradeCenter() 44 | normal_worker = NormalWorker() 45 | upgrade_center.register(normal_worker) 46 | upgrade_center.start() 47 | upgrade_center.stop() 48 | 49 | self.assertTrue(normal_worker.is_stopped, 'Worker is stopped') 50 | 51 | def test_more_workers(self): 52 | upgrade_center = UpgradeCenter() 53 | upgrade_center.register(NormalWorker()) 54 | upgrade_center.register(NormalWorker()) 55 | upgrade_center.on('finish', self.handle_done) 56 | upgrade_center.start() 57 | 58 | def test_more_workers_stop(self): 59 | upgrade_center = UpgradeCenter() 60 | normal_worker1 = NormalWorker() 61 | normal_worker2 = NormalWorker() 62 | upgrade_center.register(normal_worker1) 63 | upgrade_center.register(normal_worker2) 64 | upgrade_center.start() 65 | upgrade_center.stop() 66 | self.assertEqual( 67 | [normal_worker1.is_stopped, normal_worker2.is_stopped], 68 | [True, True]) 69 | 70 | def test_with_error_worker(self): 71 | upgrade_center = UpgradeCenter() 72 | upgrade_center.register(ErrorWorker()) 73 | upgrade_center.register(NormalWorker()) 74 | upgrade_center.on('error', self.handle_error) 75 | upgrade_center.start() 76 | 77 | # TODO: test the firmware work with mock communicator 78 | def test_firmware_worker(self): 79 | pass 80 | 81 | def test_before_after_workers(self): 82 | upgrade_center = UpgradeCenter() 83 | before_worker = NormalWorker() 84 | before_worker.group = UPGRADE_GROUP.BEFORE_ALL 85 | upgrade_center.register(before_worker) 86 | after_worker = NormalWorker() 87 | after_worker.group = UPGRADE_GROUP.AFTER_ALL 88 | upgrade_center.register(after_worker) 89 | 90 | upgrade_center.register(NormalWorker()) 91 | upgrade_center.register(NormalWorker()) 92 | upgrade_center.register(NormalWorker()) 93 | 94 | upgrade_center.on('finish', self.handle_done) 95 | upgrade_center.start() 96 | 97 | if __name__ == '__main__': 98 | unittest.main() -------------------------------------------------------------------------------- /tests/test_webserver.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | import threading 4 | import time 5 | import asyncio 6 | import tornado.ioloop 7 | from websocket import create_connection 8 | 9 | try: 10 | from aceinna.core.tunnel_web import WebServer 11 | from aceinna.models import WebserverArgs 12 | except: # pylint: disable=bare-except 13 | sys.path.append('./src') 14 | from aceinna.core.tunnel_web import WebServer 15 | from aceinna.models import WebserverArgs 16 | 17 | 18 | WS_ADDRESS = "ws://127.0.0.1:8000" 19 | 20 | 21 | # pylint: disable=missing-class-docstring 22 | # @unittest.skip 23 | class TestWebserver(unittest.TestCase): 24 | _tunnel=None 25 | 26 | def setUp(self): 27 | pass 28 | 29 | def tearDown(self): 30 | pass 31 | 32 | def test_websocket_server_establish(self): 33 | # start web server 34 | threading.Thread(target=self._prepare_tunnel).start() 35 | time.sleep(1) 36 | # send a ping request 37 | websocket_client = create_connection(WS_ADDRESS) 38 | # wait the response 39 | response = websocket_client.recv() 40 | websocket_client.close() 41 | 42 | self._tunnel.stop() 43 | # validate the result 44 | self.assertTrue(response is not None, 'WebSocket server established') 45 | 46 | def _prepare_tunnel(self): 47 | asyncio.set_event_loop(asyncio.new_event_loop()) 48 | 49 | event_loop = tornado.ioloop.IOLoop.current() 50 | 51 | self._tunnel = WebServer(WebserverArgs(), event_loop) 52 | self._tunnel.setup() 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /tools/ci_notify.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import smtplib 4 | import sys 5 | from email.mime.text import MIMEText 6 | from email.mime.multipart import MIMEMultipart 7 | from email.header import Header 8 | 9 | try: 10 | from aceinna import VERSION 11 | except: # pylint: disable=bare-except 12 | sys.path.append('./src') 13 | from aceinna import VERSION 14 | 15 | SYS = platform.system() 16 | SENDER = os.environ['EMAIL_ADDRESS'] 17 | RECEIVERS = ['ywsong@aceinna.com'] 18 | PASSWORD = os.environ['EMAIL_PASSWORD'] 19 | 20 | MESSAGE = MIMEMultipart() 21 | MESSAGE['From'] = Header("notifications@aceinna.com", 'utf-8') 22 | MESSAGE['To'] = ";".join(RECEIVERS) 23 | SUBJECT = '[{0}] CI Executable'.format(SYS) 24 | MESSAGE['Subject'] = Header(SUBJECT, 'utf-8') 25 | 26 | MESSAGE.attach( 27 | MIMEText('

Built executable on {0}

Version: {1}

'.format(SYS, VERSION), 'html', 'utf-8')) 28 | 29 | FILE_NAME = 'ans-devices.exe' if SYS == "Windows" else 'ans-devices' 30 | 31 | ATTACHMENT = MIMEText(open(os.path.join(os.getcwd(), 'dist', FILE_NAME), 32 | 'rb').read(), 'base64', 'utf-8') 33 | ATTACHMENT["Content-Type"] = 'application/octet-stream' 34 | ATTACHMENT["Content-Disposition"] = 'attachment; filename="{0}"'.format( 35 | FILE_NAME) 36 | MESSAGE.attach(ATTACHMENT) 37 | 38 | try: 39 | smtp_client = smtplib.SMTP('smtp.office365.com', 587) 40 | smtp_client.ehlo() 41 | smtp_client.starttls() 42 | smtp_client.login(SENDER, PASSWORD) 43 | smtp_client.sendmail(SENDER, RECEIVERS, MESSAGE.as_string()) 44 | except smtplib.SMTPException as error: 45 | print(error) 46 | -------------------------------------------------------------------------------- /tools/open_packet_parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import sys 4 | try: 5 | from aceinna.devices.parsers.open_packet_parser import ( 6 | match_command_handler, common_continuous_parser) 7 | except: # pylint: disable=bare-except 8 | sys.path.append('./src') 9 | from aceinna.devices.parsers.open_packet_parser import ( 10 | match_command_handler, common_continuous_parser) 11 | 12 | 13 | APP_NAME = 'IMU' 14 | APP_FILE_PATH = os.path.join( 15 | os.getcwd(), 'setting', 'openimu', APP_NAME, 'openimu.json') 16 | PROPERTIES = None 17 | with open(APP_FILE_PATH) as json_data: 18 | PROPERTIES = json.load(json_data) 19 | 20 | 21 | def parse_command_data(packet_type, payload): 22 | payload_parser = match_command_handler(packet_type) 23 | return payload_parser(payload, PROPERTIES['userConfiguration']) 24 | 25 | 26 | def parse_continuous_data(packet_type, payload): 27 | payload_parser = common_continuous_parser(packet_type) 28 | output_packet_config = next( 29 | (x for x in PROPERTIES['userMessages']['outputPackets'] 30 | if x['name'] == packet_type), None) 31 | 32 | return payload_parser(payload, output_packet_config) 33 | 34 | 35 | if __name__ == '__main__': 36 | # raw data need to be parsed, only need payload, don't contains prefix, packet type, packet length 37 | sample_data = [0xC7, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x58, 0x2B, 0x59, 0x2B, 0x5A, 0x00, 0x00, 38 | 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4B] 39 | # parse command data 40 | print(parse_command_data('gA', sample_data)) 41 | 42 | # parse continuous dataa 43 | # print(parse_continuous_data('z1', [])) 44 | --------------------------------------------------------------------------------