pymammotion 0.2.28__py3-none-any.whl → 0.2.30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymammotion/__init__.py +12 -9
- pymammotion/aliyun/cloud_gateway.py +57 -39
- pymammotion/aliyun/cloud_service.py +3 -3
- pymammotion/aliyun/dataclass/dev_by_account_response.py +1 -2
- pymammotion/aliyun/dataclass/session_by_authcode_response.py +1 -0
- pymammotion/bluetooth/ble.py +6 -6
- pymammotion/bluetooth/ble_message.py +30 -16
- pymammotion/bluetooth/data/convert.py +1 -1
- pymammotion/bluetooth/data/framectrldata.py +1 -1
- pymammotion/bluetooth/data/notifydata.py +6 -6
- pymammotion/const.py +1 -0
- pymammotion/data/model/__init__.py +2 -0
- pymammotion/data/model/account.py +1 -1
- pymammotion/data/model/device.py +31 -24
- pymammotion/data/model/device_config.py +71 -0
- pymammotion/data/model/enums.py +4 -4
- pymammotion/data/model/excute_boarder_params.py +5 -5
- pymammotion/data/model/execute_boarder.py +4 -4
- pymammotion/data/model/generate_route_information.py +18 -124
- pymammotion/data/model/hash_list.py +4 -7
- pymammotion/data/model/location.py +3 -3
- pymammotion/data/model/mowing_modes.py +1 -1
- pymammotion/data/model/plan.py +4 -4
- pymammotion/data/model/region_data.py +4 -4
- pymammotion/data/model/report_info.py +1 -1
- pymammotion/data/mqtt/event.py +8 -3
- pymammotion/data/state_manager.py +13 -12
- pymammotion/event/event.py +14 -14
- pymammotion/http/http.py +33 -45
- pymammotion/http/model/http.py +75 -0
- pymammotion/mammotion/commands/messages/driver.py +20 -23
- pymammotion/mammotion/commands/messages/navigation.py +47 -48
- pymammotion/mammotion/commands/messages/network.py +17 -35
- pymammotion/mammotion/commands/messages/system.py +6 -7
- pymammotion/mammotion/control/joystick.py +11 -10
- pymammotion/mammotion/devices/__init__.py +2 -2
- pymammotion/mammotion/devices/base.py +248 -0
- pymammotion/mammotion/devices/mammotion.py +52 -1042
- pymammotion/mammotion/devices/mammotion_bluetooth.py +447 -0
- pymammotion/mammotion/devices/mammotion_cloud.py +244 -0
- pymammotion/mqtt/mammotion_future.py +3 -2
- pymammotion/mqtt/mammotion_mqtt.py +23 -23
- pymammotion/proto/__init__.py +6 -0
- pymammotion/utility/constant/__init__.py +3 -1
- pymammotion/utility/conversions.py +1 -1
- pymammotion/utility/datatype_converter.py +9 -9
- pymammotion/utility/device_type.py +47 -18
- pymammotion/utility/map.py +2 -2
- pymammotion/utility/movement.py +2 -1
- pymammotion/utility/periodic.py +5 -5
- pymammotion/utility/rocker_util.py +1 -1
- {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/METADATA +3 -1
- {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/RECORD +55 -51
- {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/LICENSE +0 -0
- {pymammotion-0.2.28.dist-info → pymammotion-0.2.30.dist-info}/WHEEL +0 -0
pymammotion/data/model/device.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
"""MowingDevice class to wrap around the betterproto dataclasses."""
|
2
2
|
|
3
|
-
import math
|
4
3
|
from dataclasses import dataclass
|
5
4
|
|
6
5
|
import betterproto
|
@@ -16,8 +15,13 @@ from pymammotion.proto.mctrl_driver import MctlDriver
|
|
16
15
|
from pymammotion.proto.mctrl_nav import MctlNav
|
17
16
|
from pymammotion.proto.mctrl_ota import MctlOta
|
18
17
|
from pymammotion.proto.mctrl_pept import MctlPept
|
19
|
-
from pymammotion.proto.mctrl_sys import
|
20
|
-
|
18
|
+
from pymammotion.proto.mctrl_sys import (
|
19
|
+
MctlSys,
|
20
|
+
MowToAppInfoT,
|
21
|
+
ReportInfoData,
|
22
|
+
SystemRapidStateTunnelMsg,
|
23
|
+
SystemUpdateBufMsg,
|
24
|
+
)
|
21
25
|
from pymammotion.utility.constant import WorkMode
|
22
26
|
from pymammotion.utility.conversions import parse_double
|
23
27
|
from pymammotion.utility.map import CoordinateConverter
|
@@ -32,7 +36,7 @@ class MowingDevice:
|
|
32
36
|
location: Location
|
33
37
|
mowing_state: RapidState
|
34
38
|
|
35
|
-
def __init__(self):
|
39
|
+
def __init__(self) -> None:
|
36
40
|
self.device = LubaMsg()
|
37
41
|
self.map = HashList(area={}, path={}, obstacle={}, hashlist=[])
|
38
42
|
self.location = Location()
|
@@ -53,7 +57,7 @@ class MowingDevice:
|
|
53
57
|
"""Update the raw LubaMsg data."""
|
54
58
|
self.device = LubaMsg(**raw)
|
55
59
|
|
56
|
-
def buffer(self, buffer_list: SystemUpdateBufMsg):
|
60
|
+
def buffer(self, buffer_list: SystemUpdateBufMsg) -> None:
|
57
61
|
"""Update the device based on which buffer we are reading from."""
|
58
62
|
match buffer_list.update_buf_data[0]:
|
59
63
|
case 1:
|
@@ -95,7 +99,7 @@ class MowingDevice:
|
|
95
99
|
]
|
96
100
|
)
|
97
101
|
|
98
|
-
def update_report_data(self, toapp_report_data: ReportInfoData):
|
102
|
+
def update_report_data(self, toapp_report_data: ReportInfoData) -> None:
|
99
103
|
coordinate_converter = CoordinateConverter(self.location.RTK.latitude, self.location.RTK.longitude)
|
100
104
|
for index, location in enumerate(toapp_report_data.locations):
|
101
105
|
if index == 0 and location.real_pos_y != 0:
|
@@ -105,18 +109,21 @@ class MowingDevice:
|
|
105
109
|
parse_double(location.real_pos_y, 4.0), parse_double(location.real_pos_x, 4.0)
|
106
110
|
)
|
107
111
|
if location.zone_hash:
|
108
|
-
self.location.work_zone =
|
109
|
-
|
110
|
-
|
112
|
+
self.location.work_zone = (
|
113
|
+
location.zone_hash if self.report_data.dev.sys_status == WorkMode.MODE_WORKING else 0
|
114
|
+
)
|
111
115
|
|
112
116
|
self.report_data = self.report_data.from_dict(toapp_report_data.to_dict(casing=betterproto.Casing.SNAKE))
|
113
117
|
|
114
|
-
def run_state_update(self, rapid_state: SystemRapidStateTunnelMsg):
|
118
|
+
def run_state_update(self, rapid_state: SystemRapidStateTunnelMsg) -> None:
|
115
119
|
self.mowing_state = RapidState().from_raw(rapid_state.rapid_state_data)
|
116
120
|
|
117
|
-
def mow_info(self, toapp_mow_info: MowToAppInfoT):
|
121
|
+
def mow_info(self, toapp_mow_info: MowToAppInfoT) -> None:
|
118
122
|
pass
|
119
123
|
|
124
|
+
def report_missing_data(self) -> None:
|
125
|
+
"""Report missing data so we can refetch it."""
|
126
|
+
|
120
127
|
@property
|
121
128
|
def net(self):
|
122
129
|
"""Will return a wrapped betterproto of net."""
|
@@ -159,7 +166,7 @@ class DevNetData:
|
|
159
166
|
|
160
167
|
net: dict
|
161
168
|
|
162
|
-
def __init__(self, net: DevNet):
|
169
|
+
def __init__(self, net: DevNet) -> None:
|
163
170
|
if isinstance(net, dict):
|
164
171
|
self.net = net
|
165
172
|
else:
|
@@ -182,13 +189,13 @@ class SysData:
|
|
182
189
|
|
183
190
|
sys: dict
|
184
191
|
|
185
|
-
def __init__(self, sys: MctlSys):
|
192
|
+
def __init__(self, sys: MctlSys) -> None:
|
186
193
|
if isinstance(sys, dict):
|
187
194
|
self.sys = sys
|
188
195
|
else:
|
189
196
|
self.sys = sys.to_dict()
|
190
197
|
|
191
|
-
def __getattr__(self, item):
|
198
|
+
def __getattr__(self, item: str):
|
192
199
|
"""Intercept call to get sys in dict and return a betterproto dataclass."""
|
193
200
|
if self.sys.get(item) is None:
|
194
201
|
return MctlSys().__getattribute__(item)
|
@@ -205,13 +212,13 @@ class NavData:
|
|
205
212
|
|
206
213
|
nav: dict
|
207
214
|
|
208
|
-
def __init__(self, nav: MctlNav):
|
215
|
+
def __init__(self, nav: MctlNav) -> None:
|
209
216
|
if isinstance(nav, dict):
|
210
217
|
self.nav = nav
|
211
218
|
else:
|
212
219
|
self.nav = nav.to_dict()
|
213
220
|
|
214
|
-
def __getattr__(self, item):
|
221
|
+
def __getattr__(self, item: str):
|
215
222
|
"""Intercept call to get nav in dict and return a betterproto dataclass."""
|
216
223
|
if self.nav.get(item) is None:
|
217
224
|
return MctlNav().__getattribute__(item)
|
@@ -228,13 +235,13 @@ class DriverData:
|
|
228
235
|
|
229
236
|
driver: dict
|
230
237
|
|
231
|
-
def __init__(self, driver: MctlDriver):
|
238
|
+
def __init__(self, driver: MctlDriver) -> None:
|
232
239
|
if isinstance(driver, dict):
|
233
240
|
self.driver = driver
|
234
241
|
else:
|
235
242
|
self.driver = driver.to_dict()
|
236
243
|
|
237
|
-
def __getattr__(self, item):
|
244
|
+
def __getattr__(self, item: str):
|
238
245
|
"""Intercept call to get driver in dict and return a betterproto dataclass."""
|
239
246
|
if self.driver.get(item) is None:
|
240
247
|
return MctlDriver().__getattribute__(item)
|
@@ -251,13 +258,13 @@ class MulData:
|
|
251
258
|
|
252
259
|
mul: dict
|
253
260
|
|
254
|
-
def __init__(self, mul: SocMul):
|
261
|
+
def __init__(self, mul: SocMul) -> None:
|
255
262
|
if isinstance(mul, dict):
|
256
263
|
self.mul = mul
|
257
264
|
else:
|
258
265
|
self.mul = mul.to_dict()
|
259
266
|
|
260
|
-
def __getattr__(self, item):
|
267
|
+
def __getattr__(self, item: str):
|
261
268
|
"""Intercept call to get mul in dict and return a betterproto dataclass."""
|
262
269
|
if self.mul.get(item) is None:
|
263
270
|
return SocMul().__getattribute__(item)
|
@@ -274,13 +281,13 @@ class OtaData:
|
|
274
281
|
|
275
282
|
ota: dict
|
276
283
|
|
277
|
-
def __init__(self, ota: MctlOta):
|
284
|
+
def __init__(self, ota: MctlOta) -> None:
|
278
285
|
if isinstance(ota, dict):
|
279
286
|
self.ota = ota
|
280
287
|
else:
|
281
288
|
self.ota = ota.to_dict()
|
282
289
|
|
283
|
-
def __getattr__(self, item):
|
290
|
+
def __getattr__(self, item: str):
|
284
291
|
"""Intercept call to get ota in dict and return a betterproto dataclass."""
|
285
292
|
if self.ota.get(item) is None:
|
286
293
|
return MctlOta().__getattribute__(item)
|
@@ -297,13 +304,13 @@ class PeptData:
|
|
297
304
|
|
298
305
|
pept: dict
|
299
306
|
|
300
|
-
def __init__(self, pept: MctlPept):
|
307
|
+
def __init__(self, pept: MctlPept) -> None:
|
301
308
|
if isinstance(pept, dict):
|
302
309
|
self.pept = pept
|
303
310
|
else:
|
304
311
|
self.pept = pept.to_dict()
|
305
312
|
|
306
|
-
def __getattr__(self, item):
|
313
|
+
def __getattr__(self, item: str):
|
307
314
|
"""Intercept call to get pept in dict and return a betterproto dataclass."""
|
308
315
|
if self.pept.get(item) is None:
|
309
316
|
return MctlPept().__getattribute__(item)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
|
3
|
+
from pymammotion.utility.device_type import DeviceType
|
4
|
+
|
3
5
|
|
4
6
|
@dataclass
|
5
7
|
class DeviceLimits:
|
@@ -7,3 +9,72 @@ class DeviceLimits:
|
|
7
9
|
blade_height_max: int
|
8
10
|
working_speed_min: float
|
9
11
|
working_speed_max: float
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class OperationSettings:
|
16
|
+
"""Operation settings for a device."""
|
17
|
+
|
18
|
+
is_mow: bool = True
|
19
|
+
is_dump: bool = True
|
20
|
+
is_edge: bool = False
|
21
|
+
collect_grass_frequency: int = 10
|
22
|
+
job_mode: int = 0 # taskMode
|
23
|
+
job_version: int = 0
|
24
|
+
job_id: int = 0
|
25
|
+
speed: float = 0.3
|
26
|
+
ultra_wave: int = 2 # touch no touch etc
|
27
|
+
channel_mode: int = 0 # line mode is grid single double or single2
|
28
|
+
channel_width: int = 25
|
29
|
+
rain_tactics: int = 0
|
30
|
+
blade_height: int = 0
|
31
|
+
path_order: str = ""
|
32
|
+
toward: int = 0 # is just angle
|
33
|
+
toward_included_angle: int = 0
|
34
|
+
toward_mode: int = 0 # angle type relative etc
|
35
|
+
border_mode: int = 1 # border laps
|
36
|
+
obstacle_laps: int = 1
|
37
|
+
start_progress: int = 0
|
38
|
+
|
39
|
+
|
40
|
+
def create_path_order(operation_mode: OperationSettings, device_name: str) -> str:
|
41
|
+
i = 8
|
42
|
+
bArr = bytearray(8)
|
43
|
+
bArr[0] = operation_mode.border_mode
|
44
|
+
bArr[1] = operation_mode.obstacle_laps
|
45
|
+
bArr[3] = operation_mode.start_progress
|
46
|
+
bArr[2] = 0
|
47
|
+
|
48
|
+
if not DeviceType.is_luba1(device_name):
|
49
|
+
bArr[4] = 0
|
50
|
+
if DeviceType.is_yuka(device_name):
|
51
|
+
i = calculate_yuka_mode(operation_mode)
|
52
|
+
elif not DeviceType.is_luba_2(device_name):
|
53
|
+
i = 0
|
54
|
+
bArr[5] = i
|
55
|
+
if operation_mode.is_dump:
|
56
|
+
b = operation_mode.collect_grass_frequency
|
57
|
+
else:
|
58
|
+
b = 10
|
59
|
+
bArr[6] = b
|
60
|
+
if DeviceType.is_luba1(device_name):
|
61
|
+
bArr[4] = operation_mode.toward_mode
|
62
|
+
return str(bArr, "UTF-8")
|
63
|
+
|
64
|
+
|
65
|
+
def calculate_yuka_mode(operation_mode: OperationSettings) -> int:
|
66
|
+
if operation_mode.is_mow and operation_mode.is_dump and operation_mode.is_edge:
|
67
|
+
return 14
|
68
|
+
if operation_mode.is_mow and operation_mode.is_dump and not operation_mode.is_edge:
|
69
|
+
return 12
|
70
|
+
if operation_mode.is_mow and not operation_mode.is_dump and operation_mode.is_edge:
|
71
|
+
return 10
|
72
|
+
if operation_mode.is_mow and not operation_mode.is_dump and not operation_mode.is_edge:
|
73
|
+
return 8
|
74
|
+
if not operation_mode.is_mow and operation_mode.is_dump and operation_mode.is_edge:
|
75
|
+
return 6
|
76
|
+
if not operation_mode.is_mow and not operation_mode.is_dump and operation_mode.is_edge:
|
77
|
+
return 2
|
78
|
+
if not operation_mode.is_mow and operation_mode.is_dump and not operation_mode.is_edge:
|
79
|
+
return 4
|
80
|
+
return 0
|
pymammotion/data/model/enums.py
CHANGED
@@ -9,7 +9,7 @@ class PositionMode(Enum):
|
|
9
9
|
UNKNOWN = 4
|
10
10
|
|
11
11
|
@staticmethod
|
12
|
-
def from_value(value):
|
12
|
+
def from_value(value: int):
|
13
13
|
if value == 0:
|
14
14
|
return PositionMode.FIX
|
15
15
|
elif value == 1:
|
@@ -21,7 +21,7 @@ class PositionMode(Enum):
|
|
21
21
|
else:
|
22
22
|
return PositionMode.UNKNOWN
|
23
23
|
|
24
|
-
def __str__(self):
|
24
|
+
def __str__(self) -> str:
|
25
25
|
if self == PositionMode.FIX:
|
26
26
|
return "Fix"
|
27
27
|
elif self == PositionMode.SINGLE:
|
@@ -42,7 +42,7 @@ class RTKStatus(Enum):
|
|
42
42
|
UNKNOWN = 6
|
43
43
|
|
44
44
|
@staticmethod
|
45
|
-
def from_value(value):
|
45
|
+
def from_value(value: int):
|
46
46
|
if value == 0:
|
47
47
|
return RTKStatus.NONE
|
48
48
|
elif value == 1 or value == 2:
|
@@ -54,7 +54,7 @@ class RTKStatus(Enum):
|
|
54
54
|
else:
|
55
55
|
return RTKStatus.UNKNOWN
|
56
56
|
|
57
|
-
def __str__(self):
|
57
|
+
def __str__(self) -> str:
|
58
58
|
if self == RTKStatus.NONE:
|
59
59
|
return "None"
|
60
60
|
elif self == RTKStatus.SINGLE:
|
@@ -5,7 +5,7 @@ class ExecuteBorderParams:
|
|
5
5
|
currentFrame = 0
|
6
6
|
jobIndex = ""
|
7
7
|
|
8
|
-
def __init__(self, i, str_, list_):
|
8
|
+
def __init__(self, i, str_, list_) -> None:
|
9
9
|
"""Generated source for method __init__"""
|
10
10
|
self.currentFrame = i
|
11
11
|
self.border = list_
|
@@ -15,7 +15,7 @@ class ExecuteBorderParams:
|
|
15
15
|
"""Generated source for method getCurrentFrame"""
|
16
16
|
return self.currentFrame
|
17
17
|
|
18
|
-
def set_current_frame(self, i):
|
18
|
+
def set_current_frame(self, i) -> None:
|
19
19
|
"""Generated source for method setCurrentFrame"""
|
20
20
|
self.currentFrame = i
|
21
21
|
|
@@ -23,7 +23,7 @@ class ExecuteBorderParams:
|
|
23
23
|
"""Generated source for method getJobIndex"""
|
24
24
|
return self.jobIndex
|
25
25
|
|
26
|
-
def set_job_index(self, str_):
|
26
|
+
def set_job_index(self, str_) -> None:
|
27
27
|
"""Generated source for method setJobIndex"""
|
28
28
|
self.jobIndex = str_
|
29
29
|
|
@@ -31,11 +31,11 @@ class ExecuteBorderParams:
|
|
31
31
|
"""Generated source for method getBorder"""
|
32
32
|
return self.border
|
33
33
|
|
34
|
-
def set_border(self, border_list):
|
34
|
+
def set_border(self, border_list) -> None:
|
35
35
|
"""Generated source for method setBorder"""
|
36
36
|
self.border = border_list
|
37
37
|
|
38
|
-
def __str__(self):
|
38
|
+
def __str__(self) -> str:
|
39
39
|
"""Generated source for method toString"""
|
40
40
|
return (
|
41
41
|
"ExecuteBorderParamsBean{currentFrame="
|
@@ -9,7 +9,7 @@ class ExecuteBorder(Serializable):
|
|
9
9
|
cmd = 0
|
10
10
|
params = ExecuteBorderParams(None, None, None)
|
11
11
|
|
12
|
-
def __init__(self, i, execute_border_params: ExecuteBorderParams):
|
12
|
+
def __init__(self, i, execute_border_params: ExecuteBorderParams) -> None:
|
13
13
|
"""Generated source for method __init__"""
|
14
14
|
super().__init__()
|
15
15
|
self.cmd = i
|
@@ -19,7 +19,7 @@ class ExecuteBorder(Serializable):
|
|
19
19
|
"""Generated source for method getCmd"""
|
20
20
|
return self.cmd
|
21
21
|
|
22
|
-
def set_cmd(self, i):
|
22
|
+
def set_cmd(self, i) -> None:
|
23
23
|
"""Generated source for method setCmd"""
|
24
24
|
self.cmd = i
|
25
25
|
|
@@ -27,10 +27,10 @@ class ExecuteBorder(Serializable):
|
|
27
27
|
"""Generated source for method getParams"""
|
28
28
|
return self.params
|
29
29
|
|
30
|
-
def set_params(self, execute_border_params):
|
30
|
+
def set_params(self, execute_border_params) -> None:
|
31
31
|
"""Generated source for method setParams"""
|
32
32
|
self.params = execute_border_params
|
33
33
|
|
34
|
-
def __str__(self):
|
34
|
+
def __str__(self) -> str:
|
35
35
|
"""Generated source for method toString"""
|
36
36
|
return "ExecuteBean{cmd=" + self.cmd + ", params=" + self.params + "}"
|
@@ -1,133 +1,27 @@
|
|
1
1
|
import logging
|
2
|
+
from dataclasses import dataclass
|
2
3
|
from typing import List
|
3
4
|
|
4
5
|
logger = logging.getLogger(__name__)
|
5
6
|
|
6
7
|
|
8
|
+
@dataclass
|
7
9
|
class GenerateRouteInformation:
|
8
10
|
"""Creates a model for generating route information and mowing plan before starting a job."""
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
self.one_hashs = one_hashs
|
27
|
-
self.rain_tactics = rain_tactics
|
28
|
-
self.job_mode = job_mode
|
29
|
-
self.knife_height = knife_height
|
30
|
-
self.speed = speed
|
31
|
-
self.ultra_wave = ultra_wave
|
32
|
-
self.channel_width = channel_width
|
33
|
-
self.channel_mode = channel_mode
|
34
|
-
self.toward = toward
|
35
|
-
self.edge_mode = rain_tactics
|
36
|
-
self.path_order = path_order
|
37
|
-
self.toward_included_angle = toward_included_angle
|
38
|
-
logger.debug(
|
39
|
-
f"Mode route command parameters jobMode={job_mode}//channelWidth={channel_width}//speed={speed}//UltraWave={ultra_wave}//channelMode={channel_mode}//edgeMode={rain_tactics}//knifeHeight={knife_height} pathOrder:{path_order.encode('utf-8')}"
|
40
|
-
)
|
41
|
-
|
42
|
-
def get_job_id(self) -> int:
|
43
|
-
return self.job_id
|
44
|
-
|
45
|
-
def set_job_id(self, job_id: int) -> None:
|
46
|
-
self.job_id = job_id
|
47
|
-
|
48
|
-
def get_job_ver(self) -> int:
|
49
|
-
return self.job_ver
|
50
|
-
|
51
|
-
def set_job_ver(self, job_ver: int) -> None:
|
52
|
-
self.job_ver = job_ver
|
53
|
-
|
54
|
-
def get_rain_tactics(self) -> int:
|
55
|
-
return self.rain_tactics
|
56
|
-
|
57
|
-
def set_rain_tactics(self, rain_tactics: int) -> None:
|
58
|
-
self.rain_tactics = rain_tactics
|
59
|
-
|
60
|
-
def get_job_mode(self) -> int:
|
61
|
-
return self.job_mode
|
62
|
-
|
63
|
-
def set_job_mode(self, job_mode: int) -> None:
|
64
|
-
self.job_mode = job_mode
|
65
|
-
|
66
|
-
def get_knife_height(self) -> int:
|
67
|
-
return self.knife_height
|
68
|
-
|
69
|
-
def set_knife_height(self, knife_height: int) -> None:
|
70
|
-
self.knife_height = knife_height
|
71
|
-
|
72
|
-
def get_speed(self) -> float:
|
73
|
-
return self.speed
|
74
|
-
|
75
|
-
def set_speed(self, speed: float) -> None:
|
76
|
-
self.speed = speed
|
77
|
-
|
78
|
-
def get_ultra_wave(self) -> int:
|
79
|
-
return self.ultra_wave
|
80
|
-
|
81
|
-
def set_ultra_wave(self, ultra_wave: int) -> None:
|
82
|
-
self.ultra_wave = ultra_wave
|
83
|
-
|
84
|
-
def get_channel_width(self) -> int:
|
85
|
-
return self.channel_width
|
86
|
-
|
87
|
-
def set_channel_width(self, channel_width: int) -> None:
|
88
|
-
self.channel_width = channel_width
|
89
|
-
|
90
|
-
def get_channel_mode(self) -> int:
|
91
|
-
return self.channel_mode
|
92
|
-
|
93
|
-
def set_channel_mode(self, channel_mode: int) -> None:
|
94
|
-
self.channel_mode = channel_mode
|
95
|
-
|
96
|
-
def get_toward(self) -> int:
|
97
|
-
return self.toward
|
98
|
-
|
99
|
-
def set_toward(self, toward: int) -> None:
|
100
|
-
self.toward = toward
|
101
|
-
|
102
|
-
def get_one_hashs(self) -> List[int]:
|
103
|
-
return self.one_hashs if self.one_hashs else []
|
104
|
-
|
105
|
-
def set_one_hashs(self, one_hashs: List[int]) -> None:
|
106
|
-
self.one_hashs = one_hashs
|
107
|
-
|
108
|
-
def get_path_order(self) -> str:
|
109
|
-
return self.path_order
|
110
|
-
|
111
|
-
def set_path_order(self, path_order: str) -> None:
|
112
|
-
self.path_order = path_order
|
113
|
-
|
114
|
-
def get_toward_included_angle(self) -> int:
|
115
|
-
return self.toward_included_angle
|
116
|
-
|
117
|
-
def set_toward_included_angle(self, toward_included_angle: int) -> None:
|
118
|
-
self.toward_included_angle = toward_included_angle
|
119
|
-
|
120
|
-
def get_toward_mode(self) -> int:
|
121
|
-
return self.toward_mode
|
122
|
-
|
123
|
-
def get_edge_mode(self) -> int:
|
124
|
-
return self.edge_mode
|
125
|
-
|
126
|
-
def set_edge_mode(self, edge_mode: int) -> None:
|
127
|
-
self.edge_mode = edge_mode
|
128
|
-
|
129
|
-
def __str__(self) -> str:
|
130
|
-
try:
|
131
|
-
return f"GenerateRouteInformation{{oneHashs={self.one_hashs}, jobId={self.job_id}, jobVer={self.job_ver}, rainTactics={self.rain_tactics}, jobMode={self.job_mode}, knifeHeight={self.knife_height}, speed={self.speed}, UltraWave={self.ultra_wave}, channelWidth={self.channel_width}, channelMode={self.channel_mode}, toward={self.toward}, pathOrder='{self.path_order.encode('utf-8')}', edgeMode={self.edge_mode}, towardIncludedAngle={self.toward_included_angle}}}"
|
132
|
-
except Exception as e:
|
133
|
-
return str(e)
|
12
|
+
one_hashs: list[int] = list
|
13
|
+
job_mode: int = 0 # taskMode
|
14
|
+
job_version: int = 0
|
15
|
+
job_id: int = 0
|
16
|
+
speed: float = 0.3
|
17
|
+
ultra_wave: int = 2 # touch no touch etc
|
18
|
+
channel_mode: int = 0 # line mode is grid single double or single2
|
19
|
+
channel_width: int = 25
|
20
|
+
rain_tactics: int = 0
|
21
|
+
blade_height: int = 0
|
22
|
+
path_order: str = ""
|
23
|
+
toward: int = 0 # is just angle
|
24
|
+
toward_included_angle: int = 0
|
25
|
+
toward_mode: int = 0 # angle type relative etc
|
26
|
+
edge_mode: int = 1 # border laps
|
27
|
+
obstacle_laps: int = 1
|
@@ -6,10 +6,12 @@ from pymammotion.proto.mctrl_nav import NavGetCommDataAck
|
|
6
6
|
|
7
7
|
class PathType(IntEnum):
|
8
8
|
"""Path types for common data."""
|
9
|
+
|
9
10
|
AREA = 0
|
10
11
|
OBSTACLE = 1
|
11
12
|
PATH = 2
|
12
13
|
|
14
|
+
|
13
15
|
@dataclass
|
14
16
|
class FrameList:
|
15
17
|
total_frame: int
|
@@ -28,13 +30,12 @@ class HashList:
|
|
28
30
|
obstacle: dict # type 1
|
29
31
|
hashlist: list[int]
|
30
32
|
|
31
|
-
def set_hashlist(self, hashlist: list[int]):
|
33
|
+
def set_hashlist(self, hashlist: list[int]) -> None:
|
32
34
|
self.hashlist = hashlist
|
33
35
|
self.area = {hash_id: frames for hash_id, frames in self.area.items() if hash_id in hashlist}
|
34
36
|
self.path = {hash_id: frames for hash_id, frames in self.path.items() if hash_id in hashlist}
|
35
37
|
self.obstacle = {hash_id: frames for hash_id, frames in self.obstacle.items() if hash_id in hashlist}
|
36
38
|
|
37
|
-
|
38
39
|
def missing_frame(self, hash_data: NavGetCommDataAck) -> list[int]:
|
39
40
|
if hash_data.type == PathType.AREA:
|
40
41
|
return self._find_missing_frames(self.area.get(hash_data.hash))
|
@@ -45,8 +46,6 @@ class HashList:
|
|
45
46
|
if hash_data.type == PathType.PATH:
|
46
47
|
return self._find_missing_frames(self.path.get(hash_data.hash))
|
47
48
|
|
48
|
-
|
49
|
-
|
50
49
|
def update(self, hash_data: NavGetCommDataAck) -> bool:
|
51
50
|
"""Update the map data."""
|
52
51
|
if hash_data.type == PathType.AREA:
|
@@ -58,18 +57,16 @@ class HashList:
|
|
58
57
|
if hash_data.type == PathType.PATH:
|
59
58
|
return self._add_hash_data(self.path, hash_data)
|
60
59
|
|
61
|
-
|
62
60
|
@staticmethod
|
63
61
|
def _find_missing_frames(frame_list: FrameList) -> list[int]:
|
64
62
|
if frame_list.total_frame == len(frame_list.data):
|
65
63
|
return []
|
66
|
-
number_list = list(range(1, frame_list.total_frame+1))
|
64
|
+
number_list = list(range(1, frame_list.total_frame + 1))
|
67
65
|
|
68
66
|
current_frames = {frame.current_frame for frame in frame_list.data}
|
69
67
|
missing_numbers = [num for num in number_list if num not in current_frames]
|
70
68
|
return missing_numbers
|
71
69
|
|
72
|
-
|
73
70
|
@staticmethod
|
74
71
|
def _add_hash_data(hash_dict: dict, hash_data: NavGetCommDataAck) -> bool:
|
75
72
|
if hash_dict.get(hash_data.hash) is None:
|
@@ -10,7 +10,7 @@ class Point:
|
|
10
10
|
latitude: float = 0.0
|
11
11
|
longitude: float = 0.0
|
12
12
|
|
13
|
-
def __init__(self, latitude=0.0, longitude=0.0):
|
13
|
+
def __init__(self, latitude: float = 0.0, longitude: float = 0.0) -> None:
|
14
14
|
self.latitude = latitude
|
15
15
|
self.longitude = longitude
|
16
16
|
|
@@ -30,10 +30,10 @@ class Location:
|
|
30
30
|
RTK: Point
|
31
31
|
dock: Dock
|
32
32
|
position_type: int = 0
|
33
|
-
orientation: int = 0
|
33
|
+
orientation: int = 0 # 360 degree rotation +-
|
34
34
|
work_zone: int = 0
|
35
35
|
|
36
|
-
def __init__(self):
|
36
|
+
def __init__(self) -> None:
|
37
37
|
self.device = Point()
|
38
38
|
self.RTK = Point()
|
39
39
|
self.dock = Dock()
|
pymammotion/data/model/plan.py
CHANGED
@@ -2,7 +2,7 @@ from typing import List
|
|
2
2
|
|
3
3
|
|
4
4
|
class Plan:
|
5
|
-
def __init__(self):
|
5
|
+
def __init__(self) -> None:
|
6
6
|
self.pver: int = 0
|
7
7
|
self.sub_cmd: int = 0
|
8
8
|
self.area: int = 0
|
@@ -31,9 +31,9 @@ class Plan:
|
|
31
31
|
self.speed: float = 0.0
|
32
32
|
self.task_name: str = ""
|
33
33
|
self.job_name: str = ""
|
34
|
-
self.zone_hashs:
|
34
|
+
self.zone_hashs: list[int] = []
|
35
35
|
self.reserved: str = ""
|
36
|
-
self.weeks:
|
36
|
+
self.weeks: list[int] = []
|
37
37
|
self.start_date: str = ""
|
38
38
|
self.end_date: str = ""
|
39
39
|
self.job_type: int = 0
|
@@ -46,7 +46,7 @@ class Plan:
|
|
46
46
|
self.path_order: int = 0
|
47
47
|
self.demond_angle: int = 90
|
48
48
|
|
49
|
-
def __str__(self):
|
49
|
+
def __str__(self) -> str:
|
50
50
|
return f"Plan(pver={self.pver}, sub_cmd={self.sub_cmd}, area={self.area}, work_time={self.work_time}, version='{self.version}', id='{self.id}', user_id='{self.user_id}', device_id='{self.device_id}', plan_id='{self.plan_id}', task_id='{self.task_id}', job_id='{self.job_id}', start_time='{self.start_time}', end_time='{self.end_time}', week={self.week}, knife_height={self.knife_height}, model={self.model}, edge_mode={self.edge_mode}, required_time={self.required_time}, route_angle={self.route_angle}, route_model={self.route_model}, route_spacing={self.route_spacing}, ultrasonic_barrier={self.ultrasonic_barrier}, total_plan_num={self.total_plan_num}, plan_index={self.plan_index}, result={self.result}, speed={self.speed}, task_name='{self.task_name}', job_name='{self.job_name}', zone_hashs={self.zone_hashs}, reserved='{self.reserved}', weeks={self.weeks}, start_date='{self.start_date}', end_date='{self.end_date}', job_type={self.job_type}, interval_days={self.interval_days}, count_down={self.count_down}, is_enable={self.is_enable}, mowing_laps={self.mowing_laps}, path_order={self.path_order}, demond_angle={self.demond_angle}, is_mow_work={self.is_mow_work}, is_sweeping_work={self.is_sweeping_work})"
|
51
51
|
|
52
52
|
def __eq__(self, other):
|
@@ -2,7 +2,7 @@ from typing import List, Optional
|
|
2
2
|
|
3
3
|
|
4
4
|
class RegionData:
|
5
|
-
def __init__(self):
|
5
|
+
def __init__(self) -> None:
|
6
6
|
self.hash: Optional[int] = None
|
7
7
|
self.action: int = 0
|
8
8
|
self.current_frame: int = 0
|
@@ -10,7 +10,7 @@ class RegionData:
|
|
10
10
|
self.data_len: int = 0
|
11
11
|
self.p_hash_a: Optional[int] = None
|
12
12
|
self.p_hash_b: Optional[int] = None
|
13
|
-
self.path: Optional[
|
13
|
+
self.path: Optional[list[list[float]]] = None
|
14
14
|
self.pver: int = 0
|
15
15
|
self.result: int = 0
|
16
16
|
self.sub_cmd: int = 0
|
@@ -68,10 +68,10 @@ class RegionData:
|
|
68
68
|
def set_current_frame(self, current_frame: int) -> None:
|
69
69
|
self.current_frame = current_frame
|
70
70
|
|
71
|
-
def get_path(self) -> Optional[
|
71
|
+
def get_path(self) -> Optional[list[list[float]]]:
|
72
72
|
return self.path
|
73
73
|
|
74
|
-
def set_path(self, path:
|
74
|
+
def set_path(self, path: list[list[float]]) -> None:
|
75
75
|
self.path = path
|
76
76
|
|
77
77
|
def get_hash(self) -> Optional[int]:
|