[WIP] Flipper Devtool (#113)
* FLPv0.1 on Python; File transfer is working. * FLPv0.1 on Python; .gitignore fixes. * Update protocol to latest version; change interface to OOP Co-authored-by: Daniel Solmann <DanGSun@yandex.ru> Co-authored-by: coreglitch <mail@s3f.ru>
This commit is contained in:
		
							parent
							
								
									979af6c165
								
							
						
					
					
						commit
						0d7395d05c
					
				
							
								
								
									
										17
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,19 @@ | |||||||
|  | # JetBrains IDEs | ||||||
| .idea/ | .idea/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Python VirtEnvironments | ||||||
|  | .env | ||||||
|  | .venv | ||||||
|  | env/ | ||||||
|  | venv/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Python Byte-compiled / optimized files | ||||||
|  | __pycache__/ | ||||||
|  | *.py[cod] | ||||||
|  | *$py.class | ||||||
|  | 
 | ||||||
| .obj/ | .obj/ | ||||||
| target_lo/build/ | target_lo/build/ | ||||||
| target_*/build/ | target_*/build/ | ||||||
| @ -13,5 +28,3 @@ bindings/ | |||||||
| build | build | ||||||
| CMakeLists.txt | CMakeLists.txt | ||||||
| firmware/targets/f2/CMakeLists.txt | firmware/targets/f2/CMakeLists.txt | ||||||
| 
 |  | ||||||
| *.pyc |  | ||||||
							
								
								
									
										0
									
								
								flp/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								flp/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
									
										14
									
								
								flp/Pipfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								flp/Pipfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | [[source]] | ||||||
|  | name = "pypi" | ||||||
|  | url = "https://pypi.org/simple" | ||||||
|  | verify_ssl = true | ||||||
|  | 
 | ||||||
|  | [dev-packages] | ||||||
|  | black = "20.8b1" | ||||||
|  | 
 | ||||||
|  | [packages] | ||||||
|  | pyserial = "3.4" | ||||||
|  | click = "7.1.2" | ||||||
|  | 
 | ||||||
|  | [requires] | ||||||
|  | python_version = "3.8" | ||||||
							
								
								
									
										142
									
								
								flp/Pipfile.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								flp/Pipfile.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,142 @@ | |||||||
|  | { | ||||||
|  |     "_meta": { | ||||||
|  |         "hash": { | ||||||
|  |             "sha256": "62bedeef792c73b899be0e307396e579f1af2d0c1d49dbccc252cc14e11e50d4" | ||||||
|  |         }, | ||||||
|  |         "pipfile-spec": 6, | ||||||
|  |         "requires": { | ||||||
|  |             "python_version": "3.8" | ||||||
|  |         }, | ||||||
|  |         "sources": [ | ||||||
|  |             { | ||||||
|  |                 "name": "pypi", | ||||||
|  |                 "url": "https://pypi.org/simple", | ||||||
|  |                 "verify_ssl": true | ||||||
|  |             } | ||||||
|  |         ] | ||||||
|  |     }, | ||||||
|  |     "default": { | ||||||
|  |         "click": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", | ||||||
|  |                 "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" | ||||||
|  |             ], | ||||||
|  |             "index": "pypi", | ||||||
|  |             "version": "==7.1.2" | ||||||
|  |         }, | ||||||
|  |         "pyserial": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:6e2d401fdee0eab996cf734e67773a0143b932772ca8b42451440cfed942c627", | ||||||
|  |                 "sha256:e0770fadba80c31013896c7e6ef703f72e7834965954a78e71a3049488d4d7d8" | ||||||
|  |             ], | ||||||
|  |             "index": "pypi", | ||||||
|  |             "version": "==3.4" | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "develop": { | ||||||
|  |         "appdirs": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", | ||||||
|  |                 "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" | ||||||
|  |             ], | ||||||
|  |             "version": "==1.4.4" | ||||||
|  |         }, | ||||||
|  |         "black": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea", | ||||||
|  |                 "sha256:70b62ef1527c950db59062cda342ea224d772abdf6adc58b86a45421bab20a6b" | ||||||
|  |             ], | ||||||
|  |             "index": "pypi", | ||||||
|  |             "version": "==20.8b1" | ||||||
|  |         }, | ||||||
|  |         "click": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", | ||||||
|  |                 "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" | ||||||
|  |             ], | ||||||
|  |             "index": "pypi", | ||||||
|  |             "version": "==7.1.2" | ||||||
|  |         }, | ||||||
|  |         "mypy-extensions": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", | ||||||
|  |                 "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" | ||||||
|  |             ], | ||||||
|  |             "version": "==0.4.3" | ||||||
|  |         }, | ||||||
|  |         "pathspec": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0", | ||||||
|  |                 "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061" | ||||||
|  |             ], | ||||||
|  |             "version": "==0.8.0" | ||||||
|  |         }, | ||||||
|  |         "regex": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204", | ||||||
|  |                 "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162", | ||||||
|  |                 "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f", | ||||||
|  |                 "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb", | ||||||
|  |                 "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6", | ||||||
|  |                 "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7", | ||||||
|  |                 "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88", | ||||||
|  |                 "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99", | ||||||
|  |                 "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644", | ||||||
|  |                 "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a", | ||||||
|  |                 "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840", | ||||||
|  |                 "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067", | ||||||
|  |                 "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd", | ||||||
|  |                 "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4", | ||||||
|  |                 "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e", | ||||||
|  |                 "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89", | ||||||
|  |                 "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e", | ||||||
|  |                 "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc", | ||||||
|  |                 "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf", | ||||||
|  |                 "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341", | ||||||
|  |                 "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7" | ||||||
|  |             ], | ||||||
|  |             "version": "==2020.7.14" | ||||||
|  |         }, | ||||||
|  |         "toml": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", | ||||||
|  |                 "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" | ||||||
|  |             ], | ||||||
|  |             "version": "==0.10.1" | ||||||
|  |         }, | ||||||
|  |         "typed-ast": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355", | ||||||
|  |                 "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919", | ||||||
|  |                 "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa", | ||||||
|  |                 "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652", | ||||||
|  |                 "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75", | ||||||
|  |                 "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01", | ||||||
|  |                 "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d", | ||||||
|  |                 "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1", | ||||||
|  |                 "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907", | ||||||
|  |                 "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c", | ||||||
|  |                 "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3", | ||||||
|  |                 "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b", | ||||||
|  |                 "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614", | ||||||
|  |                 "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb", | ||||||
|  |                 "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b", | ||||||
|  |                 "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41", | ||||||
|  |                 "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6", | ||||||
|  |                 "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34", | ||||||
|  |                 "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe", | ||||||
|  |                 "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4", | ||||||
|  |                 "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7" | ||||||
|  |             ], | ||||||
|  |             "version": "==1.4.1" | ||||||
|  |         }, | ||||||
|  |         "typing-extensions": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", | ||||||
|  |                 "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", | ||||||
|  |                 "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" | ||||||
|  |             ], | ||||||
|  |             "version": "==3.7.4.3" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								flp/flipper/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								flp/flipper/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | from flipper import file | ||||||
|  | from flipper.file import File | ||||||
							
								
								
									
										122
									
								
								flp/flipper/file.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								flp/flipper/file.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | |||||||
|  | from enum import Enum | ||||||
|  | 
 | ||||||
|  | import serial | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RemoteIOError(IOError): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class UnsupportedOperation(RemoteIOError): | ||||||
|  |     # TODO: Tell more on error | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ReadWriteError(RemoteIOError): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class FileMode(Enum): | ||||||
|  |     READ = "r" | ||||||
|  |     WRITE = "w" | ||||||
|  |     # APPEND = "a" - Not Impl | ||||||
|  |     # READ_WRITE = "w+" | ||||||
|  |     # READ_APPEND = "a+" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class File: | ||||||
|  |     opened: bool = False | ||||||
|  |     filename: str | ||||||
|  |     conn: serial.Serial | ||||||
|  |     mode = FileMode.READ | ||||||
|  | 
 | ||||||
|  |     def _open(self): | ||||||
|  |         # Opening file on Flipper's end | ||||||
|  |         self.conn.write(f"f_open {self.filename} {self.mode}\n".encode()) | ||||||
|  | 
 | ||||||
|  |         # Checking, if everything is OK. | ||||||
|  |         self.conn.write(b"f_valid\n") | ||||||
|  |         return bool(int(self.conn.read_until().decode())) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, conn: serial.Serial, filename: str, mode=FileMode.READ): | ||||||
|  |         self.conn = conn | ||||||
|  |         self.mode = mode | ||||||
|  |         self.filename = filename | ||||||
|  | 
 | ||||||
|  |         if not self._open(): | ||||||
|  |             raise RemoteIOError | ||||||
|  |         self.opened = True | ||||||
|  | 
 | ||||||
|  |     def lseek(self, size: int): | ||||||
|  |         self.conn.write(f"f_lseek {size}\n".encode()) | ||||||
|  | 
 | ||||||
|  |     def tell(self): | ||||||
|  |         self.conn.write(b"f_tell\n") | ||||||
|  |         return int(self.conn.read_until().decode()) | ||||||
|  | 
 | ||||||
|  |     def size(self): | ||||||
|  |         self.conn.write(b"f_size\n") | ||||||
|  |         return int(self.conn.read_until().decode()) | ||||||
|  | 
 | ||||||
|  |     def write(self, data: bytes): | ||||||
|  |         if self.mode != FileMode.WRITE: | ||||||
|  |             raise UnsupportedOperation | ||||||
|  |         self.conn.write(f"f_write {len(data)}\n".encode()) | ||||||
|  |         self.conn.write(data) | ||||||
|  |         self.conn.write(b"\n") | ||||||
|  | 
 | ||||||
|  |         if self.conn.read_until().decode().strip() != "OK": | ||||||
|  |             raise ReadWriteError | ||||||
|  | 
 | ||||||
|  |     def read(self, size: int) -> bytes: | ||||||
|  |         self.conn.write(f"f_read {size}\n".encode()) | ||||||
|  |         return self.conn.read(size) | ||||||
|  | 
 | ||||||
|  |     def _close(self): | ||||||
|  |         self.conn.write(b"f_close\n") | ||||||
|  | 
 | ||||||
|  |     def close(self): | ||||||
|  |         self._close() | ||||||
|  |         # TODO: Add termination of remote file process. | ||||||
|  |         del self | ||||||
|  | 
 | ||||||
|  |     def read_all(self, block_size=128) -> bytes: | ||||||
|  |         """ | ||||||
|  |         Function to simplify reading of file over serial. | ||||||
|  | 
 | ||||||
|  |         :return: content of file represented in `bytes` | ||||||
|  |         """ | ||||||
|  |         size = self.size() | ||||||
|  |         a = bytearray() | ||||||
|  |         seek = 0 | ||||||
|  |         # Reading in blocks of block_size | ||||||
|  |         while seek < block_size * (size // block_size - 1): | ||||||
|  |             seek += block_size | ||||||
|  |             self.lseek(seek) | ||||||
|  |             a.extend(self.read(block_size)) | ||||||
|  | 
 | ||||||
|  |         seek += block_size | ||||||
|  |         self.lseek(seek) | ||||||
|  |         a.extend(self.read(size - seek))  # Appending mod | ||||||
|  | 
 | ||||||
|  |         self.lseek(0)  # Resetting seek to default-position | ||||||
|  | 
 | ||||||
|  |         return bytes(a) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def push(ser: serial.Serial, local_path, remote_path): | ||||||
|  |     with open(local_path, "rb") as local_f:  # Reading local file | ||||||
|  |         a = local_f.read() | ||||||
|  | 
 | ||||||
|  |     remote_f = File(ser, remote_path, FileMode.WRITE) | ||||||
|  |     remote_f.write(a) | ||||||
|  |     remote_f.close() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def pull(ser: serial.Serial, remote_path, local_path): | ||||||
|  |     remote_f = File(ser, remote_path, FileMode.READ) | ||||||
|  | 
 | ||||||
|  |     with open(local_path, "wb") as f: | ||||||
|  |         f.write(remote_f.read_all()) | ||||||
|  | 
 | ||||||
|  |     remote_f.close() | ||||||
							
								
								
									
										39
									
								
								flp/flp.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								flp/flp.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | import flipper | ||||||
|  | import serial | ||||||
|  | import click | ||||||
|  | 
 | ||||||
|  | DEFAULT_TTY = "/dev/ttyUSB0" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.group() | ||||||
|  | def cli(): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @cli.group() | ||||||
|  | def fs(): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @fs.command() | ||||||
|  | @click.argument("file_in", type=click.Path(exists=True)) | ||||||
|  | @click.argument("file_out") | ||||||
|  | @click.option("--port", default=DEFAULT_TTY) | ||||||
|  | def push(file_in, file_out, port): | ||||||
|  |     with serial.Serial(port, 115200, timeout=10) as p: | ||||||
|  |         flipper.file.push(p, file_in, file_out) | ||||||
|  |     print("OK.") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @fs.command() | ||||||
|  | @click.argument("file_in") | ||||||
|  | @click.argument("file_out", type=click.Path()) | ||||||
|  | @click.option("--port", default=DEFAULT_TTY) | ||||||
|  | def pull(file_in, file_out, port): | ||||||
|  |     with serial.Serial(port, 115200, timeout=10) as p: | ||||||
|  |         flipper.file.pull(p, file_in, file_out) | ||||||
|  |     print("OK.") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     cli() | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Daniel Solmann
						Daniel Solmann