moteus 0.3.57__py3-none-any.whl → 0.3.59__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.
moteus/command.py CHANGED
@@ -19,6 +19,7 @@ class Command():
19
19
  reply_required = False
20
20
  data = b''
21
21
  can_prefix = 0x0000 # a 13 bit CAN prefix
22
+ expected_reply_size = 0
22
23
 
23
24
  # If True, then the following parameters are used directly instead
24
25
  # of being calculated from destination and source (i.e. for
moteus/fdcanusb.py CHANGED
@@ -173,8 +173,11 @@ class Fdcanusb:
173
173
  bus_id = (command.destination |
174
174
  (0x8000 if command.reply_required else 0) |
175
175
  (command.can_prefix << 16))
176
+ hexdata = _hexify(command.data)
177
+ on_wire_size = self._round_up_dlc(len(command.data))
178
+ hexdata += '50' * (on_wire_size - len(command.data))
176
179
  cmd = "can send {:04x} {}{}\n".format(
177
- bus_id, _hexify(command.data), self._send_flags).encode('latin1')
180
+ bus_id, hexdata, self._send_flags).encode('latin1')
178
181
  self._serial.write(cmd)
179
182
  if self._debug_log:
180
183
  self._debug_log.write(f'{time.time()} > '.encode('latin1') +
@@ -197,3 +200,22 @@ class Fdcanusb:
197
200
  message.data = _dehexify(fields[2])
198
201
  message.arbitration_id = int(fields[1], 16)
199
202
  return message
203
+
204
+ def _round_up_dlc(self, size):
205
+ if size <= 8:
206
+ return size
207
+ if size <= 12:
208
+ return 12
209
+ if size <= 16:
210
+ return 16
211
+ if size <= 20:
212
+ return 20
213
+ if size <= 24:
214
+ return 24
215
+ if size <= 32:
216
+ return 32
217
+ if size <= 48:
218
+ return 48
219
+ if size <= 64:
220
+ return 64
221
+ return size
moteus/moteus.py CHANGED
@@ -14,6 +14,7 @@
14
14
 
15
15
  import asyncio
16
16
  import argparse
17
+ import copy
17
18
  import enum
18
19
  import importlib_metadata
19
20
  import io
@@ -606,7 +607,7 @@ class Controller:
606
607
  self._can_prefix = can_prefix
607
608
 
608
609
  # Pre-compute our query string.
609
- self._query_data = self._make_query_data()
610
+ self._query_data, self._default_query_reply_size = self._make_query_data()
610
611
 
611
612
  def _get_transport(self):
612
613
  if self.transport:
@@ -617,10 +618,15 @@ class Controller:
617
618
  self.transport = get_singleton_transport()
618
619
  return self.transport
619
620
 
620
- def _make_query_data(self):
621
+ def _make_query_data(self, query_resolution=None):
622
+ if query_resolution is None:
623
+ query_resolution = self.query_resolution
624
+
625
+ expected_reply_size = 0
626
+
621
627
  buf = io.BytesIO()
622
628
  writer = Writer(buf)
623
- qr = self.query_resolution
629
+ qr = query_resolution
624
630
  c1 = mp.WriteCombiner(writer, 0x10, int(Register.MODE), [
625
631
  qr.mode,
626
632
  qr.position,
@@ -633,6 +639,8 @@ class Controller:
633
639
  for i in range(c1.size()):
634
640
  c1.maybe_write()
635
641
 
642
+ expected_reply_size += c1.reply_size
643
+
636
644
  c2 = mp.WriteCombiner(writer, 0x10, int(Register.MOTOR_TEMPERATURE), [
637
645
  qr.motor_temperature,
638
646
  qr.trajectory_complete,
@@ -644,6 +652,8 @@ class Controller:
644
652
  for i in range(c2.size()):
645
653
  c2.maybe_write()
646
654
 
655
+ expected_reply_size += c2.reply_size
656
+
647
657
  c3 = mp.WriteCombiner(writer, 0x10, int(Register.AUX1_GPIO_STATUS), [
648
658
  qr.aux1_gpio,
649
659
  qr.aux2_gpio,
@@ -651,6 +661,8 @@ class Controller:
651
661
  for i in range(c3.size()):
652
662
  c3.maybe_write()
653
663
 
664
+ expected_reply_size += c3.reply_size
665
+
654
666
  if len(qr._extra):
655
667
  min_val = int(min(qr._extra.keys()))
656
668
  max_val = int(max(qr._extra.keys()))
@@ -660,23 +672,41 @@ class Controller:
660
672
  for i in range(min_val, max_val +1)])
661
673
  for _ in range(c4.size()):
662
674
  c4.maybe_write()
675
+ expected_reply_size += c4.reply_size
676
+
677
+ return buf.getvalue(), expected_reply_size
663
678
 
664
- return buf.getvalue()
679
+ def _format_query(self, query, query_override, data_buf, result):
680
+ if query_override is not None:
681
+ query_data, expected_reply_size = \
682
+ self._make_query_data(query_override)
683
+ data_buf.write(query_data)
684
+ result.expected_reply_size = expected_reply_size
685
+ elif query:
686
+ data_buf.write(self._query_data)
687
+ result.expected_reply_size = self._default_query_reply_size
665
688
 
666
- def _make_command(self, *, query, source=0):
689
+ def _make_command(self, *, query, query_override=None, source=0):
667
690
  result = cmd.Command()
668
691
 
669
692
  result.destination = self.id
670
693
  result.source = source
671
- result.reply_required = query
694
+ result.reply_required = query or (query_override is not None)
672
695
  result.parse = self._parser
673
696
  result.can_prefix = self._can_prefix
697
+ result.expected_reply_size = self._default_query_reply_size if query else 0
674
698
 
675
699
  return result
676
700
 
677
- def make_query(self):
678
- result = self._make_command(query=True)
679
- result.data = self._query_data
701
+ def make_query(self, query_override=None):
702
+ result = self._make_command(
703
+ query=True, query_override=query_override)
704
+ if query_override:
705
+ result.data, result.expected_reply_size = \
706
+ self._make_query_data(query_override)
707
+ else:
708
+ result.data = self._query_data
709
+ result.expected_reply_size = self._default_query_reply_size
680
710
  return result;
681
711
 
682
712
  async def query(self, **kwargs):
@@ -702,16 +732,18 @@ class Controller:
702
732
  c.maybe_write()
703
733
 
704
734
  result.data = buf.getvalue()
735
+ result.expected_reply_size = c.reply_size
705
736
  return result
706
737
 
707
738
  async def custom_query(self, *args, **kwargs):
708
739
  return await self.execute(self.make_custom_query(*args, **kwargs))
709
740
 
710
- def make_stop(self, *, query=False):
741
+ def make_stop(self, *, query=False, query_override=None):
711
742
  """Return a moteus.Command structure with data necessary to send a
712
743
  stop mode command."""
713
744
 
714
- result = self._make_command(query=query)
745
+ result = self._make_command(
746
+ query=query, query_override=query_override)
715
747
 
716
748
  data_buf = io.BytesIO()
717
749
  writer = Writer(data_buf)
@@ -719,8 +751,7 @@ class Controller:
719
751
  writer.write_int8(int(Register.MODE))
720
752
  writer.write_int8(int(Mode.STOPPED))
721
753
 
722
- if query:
723
- data_buf.write(self._query_data)
754
+ self._format_query(query, query_override, data_buf, result)
724
755
 
725
756
  result.data = data_buf.getvalue()
726
757
 
@@ -732,12 +763,14 @@ class Controller:
732
763
  def make_set_output(self, *,
733
764
  position=0.0,
734
765
  query=False,
766
+ query_override=None,
735
767
  cmd=None
736
768
  ):
737
769
  """Return a moteus.Command structure with data necessary to send a
738
770
  set output nearest command."""
739
771
 
740
- result = self._make_command(query=query)
772
+ result = self._make_command(
773
+ query=query, query_override=query_override)
741
774
 
742
775
  data_buf = io.BytesIO()
743
776
  writer = Writer(data_buf)
@@ -745,23 +778,26 @@ class Controller:
745
778
  writer.write_varuint(cmd)
746
779
  writer.write_f32(position)
747
780
 
748
- if query:
749
- data_buf.write(self._query_data)
781
+ self._format_query(query, query_override, data_buf, result)
750
782
 
751
783
  result.data = data_buf.getvalue()
752
784
  return result
753
785
 
754
786
  def make_set_output_nearest(self, *,
755
787
  position=0.0,
756
- query=False):
788
+ query=False,
789
+ query_override=None):
757
790
  return self.make_set_output(
758
- position=position, query=query, cmd=Register.SET_OUTPUT_NEAREST)
791
+ position=position, query=query, query_override=query_override,
792
+ cmd=Register.SET_OUTPUT_NEAREST)
759
793
 
760
794
  def make_set_output_exact(self, *,
761
795
  position=0.0,
762
- query=False):
796
+ query=False,
797
+ query_override=None):
763
798
  return self.make_set_output(
764
- position=position, query=query, cmd=Register.SET_OUTPUT_EXACT)
799
+ position=position, query=query, query_override=query_override,
800
+ cmd=Register.SET_OUTPUT_EXACT)
765
801
 
766
802
  async def set_output(self, *args, cmd=None, **kwargs):
767
803
  return await self.execute(self.make_set_output(**kwargs, cmd=cmd))
@@ -777,15 +813,20 @@ class Controller:
777
813
  # "make/set_rezero".
778
814
  def make_rezero(self, *,
779
815
  rezero=0.0,
780
- query=False):
816
+ query=False,
817
+ query_override=None):
781
818
  return self.make_set_output(
782
- position=rezero, query=query, cmd=Register.SET_OUTPUT_NEAREST)
819
+ position=rezero, query=query, query_override=query_override,
820
+ cmd=Register.SET_OUTPUT_NEAREST)
783
821
 
784
822
  async def set_rezero(self, *args, **kwargs):
785
823
  return await self.execute(self.make_rezero(**kwargs))
786
824
 
787
- def make_require_reindex(self):
788
- result = self._make_command(query=False)
825
+ def make_require_reindex(self,
826
+ query=False,
827
+ query_override=None):
828
+ result = self._make_command(
829
+ query=query, query_override=query_override)
789
830
 
790
831
  data_buf = io.BytesIO()
791
832
  writer = Writer(data_buf)
@@ -796,8 +837,9 @@ class Controller:
796
837
  result.data = data_buf.getvalue()
797
838
  return result
798
839
 
799
- async def set_require_reindex(self):
800
- return await self.execute(self.make_require_reindex())
840
+ async def set_require_reindex(self, query=False, query_override=None):
841
+ return await self.execute(self.make_require_reindex(
842
+ query=query, query_override=query_override))
801
843
 
802
844
  def make_position(self,
803
845
  *,
@@ -812,11 +854,13 @@ class Controller:
812
854
  velocity_limit=None,
813
855
  accel_limit=None,
814
856
  fixed_voltage_override=None,
815
- query=False):
857
+ query=False,
858
+ query_override=None):
816
859
  """Return a moteus.Command structure with data necessary to send a
817
860
  position mode command with the given values."""
818
861
 
819
- result = self._make_command(query=query)
862
+ result = self._make_command(
863
+ query=query, query_override=query_override)
820
864
 
821
865
  pr = self.position_resolution
822
866
  resolutions = [
@@ -866,8 +910,7 @@ class Controller:
866
910
  if combiner.maybe_write():
867
911
  writer.write_voltage(fixed_voltage_override, pr.fixed_voltage_override)
868
912
 
869
- if query:
870
- data_buf.write(self._query_data)
913
+ self._format_query(query, query_override, data_buf, result)
871
914
 
872
915
  result.data = data_buf.getvalue()
873
916
 
@@ -876,16 +919,51 @@ class Controller:
876
919
  async def set_position(self, *args, **kwargs):
877
920
  return await self.execute(self.make_position(**kwargs))
878
921
 
922
+ async def set_position_wait_complete(
923
+ self,
924
+ period_s=0.025,
925
+ query_override=None,
926
+ *args, **kwargs):
927
+ """Repeatedly send a position mode command to a device until it
928
+ reports that the trajectory has been completed.
929
+
930
+ If the controller is unresponsive, this method will never return.
931
+ """
932
+
933
+ if query_override is None:
934
+ query_override = copy.deepcopy(self.query_resolution)
935
+ else:
936
+ query_override = copy.deepcopy(query_override)
937
+
938
+ query_override.trajectory_complete = mp.INT8
939
+
940
+ count = 2
941
+ while True:
942
+ result = await self.set_position(
943
+ query_override=query_override, *args, **kwargs)
944
+
945
+ if result is not None:
946
+ count = max(count - 1, 0)
947
+
948
+ if (count == 0 and
949
+ result is not None and
950
+ result.values[Register.TRAJECTORY_COMPLETE]):
951
+ return result
952
+
953
+ await asyncio.sleep(period_s)
954
+
879
955
  def make_vfoc(self,
880
956
  *,
881
957
  theta,
882
958
  voltage,
883
959
  theta_rate=0.0,
884
- query=False):
960
+ query=False,
961
+ query_override=None):
885
962
  """Return a moteus.Command structure with data necessary to send a
886
963
  voltage mode FOC command."""
887
964
 
888
- result = self._make_command(query=query)
965
+ result = self._make_command(
966
+ query=query, query_override=query_override)
889
967
  cr = self.vfoc_resolution
890
968
  resolutions = [
891
969
  cr.theta if theta is not None else mp.IGNORE,
@@ -921,8 +999,7 @@ class Controller:
921
999
  if combiner.maybe_write():
922
1000
  writer.write_velocity(theta_rate / math.pi, cr.theta_rate)
923
1001
 
924
- if query:
925
- data_buf.write(self._query_data)
1002
+ self._format_query(query, query_override, data_buf, result)
926
1003
 
927
1004
  result.data = data_buf.getvalue()
928
1005
 
@@ -935,12 +1012,14 @@ class Controller:
935
1012
  *,
936
1013
  d_A,
937
1014
  q_A,
938
- query=False):
1015
+ query=False,
1016
+ query_override=None):
939
1017
  """Return a moteus.Command structure with data necessary to send a
940
1018
  current mode command.
941
1019
  """
942
1020
 
943
- result = self._make_command(query=query)
1021
+ result = self._make_command(
1022
+ query=query, query_override=query_override)
944
1023
  cr = self.current_resolution
945
1024
  resolutions = [
946
1025
  cr.d_A if d_A is not None else mp.IGNORE,
@@ -965,8 +1044,7 @@ class Controller:
965
1044
  if combiner.maybe_write():
966
1045
  writer.write_current(d_A, cr.d_A)
967
1046
 
968
- if query:
969
- data_buf.write(self._query_data)
1047
+ self._format_query(query, query_override, data_buf, result)
970
1048
 
971
1049
  result.data = data_buf.getvalue()
972
1050
 
@@ -986,11 +1064,13 @@ class Controller:
986
1064
  maximum_torque=None,
987
1065
  stop_position=None,
988
1066
  watchdog_timeout=None,
989
- query=False):
1067
+ query=False,
1068
+ query_override=None):
990
1069
  """Return a moteus.Command structure with data necessary to send a
991
1070
  within mode command with the given values."""
992
1071
 
993
- result = self._make_command(query=query)
1072
+ result = self._make_command(
1073
+ query=query, query_override=query_override)
994
1074
 
995
1075
  pr = self.position_resolution
996
1076
  resolutions = [
@@ -1029,8 +1109,7 @@ class Controller:
1029
1109
  if combiner.maybe_write():
1030
1110
  writer.write_time(watchdog_timeout, pr.watchdog_timeout)
1031
1111
 
1032
- if query:
1033
- data_buf.write(self._query_data)
1112
+ self._format_query(query, query_override, data_buf, result)
1034
1113
 
1035
1114
  result.data = data_buf.getvalue()
1036
1115
 
@@ -1039,8 +1118,9 @@ class Controller:
1039
1118
  async def set_stay_within(self, *args, **kwargs):
1040
1119
  return await self.execute(self.make_stay_within(**kwargs))
1041
1120
 
1042
- def make_brake(self, *, query=False):
1043
- result = self._make_command(query=query)
1121
+ def make_brake(self, *, query=False, query_override=None):
1122
+ result = self._make_command(
1123
+ query=query, query_override=query_override)
1044
1124
 
1045
1125
  data_buf = io.BytesIO()
1046
1126
  writer = Writer(data_buf)
@@ -1048,8 +1128,7 @@ class Controller:
1048
1128
  writer.write_int8(int(Register.MODE))
1049
1129
  writer.write_int8(int(Mode.BRAKE))
1050
1130
 
1051
- if query:
1052
- data_buf.write(self._query_data)
1131
+ self._format_query(query, query_override, data_buf, result)
1053
1132
 
1054
1133
  result.data = data_buf.getvalue()
1055
1134
 
@@ -1058,7 +1137,8 @@ class Controller:
1058
1137
  async def set_brake(self, *args, **kwargs):
1059
1138
  return await self.execute(self.make_brake(**kwargs))
1060
1139
 
1061
- def make_write_gpio(self, aux1=None, aux2=None, query=False):
1140
+ def make_write_gpio(self, aux1=None, aux2=None,
1141
+ query=False, query_override=None):
1062
1142
  """Return a moteus.Command structure with data necessary to set one or
1063
1143
  more GPIO registers.
1064
1144
 
@@ -1066,7 +1146,8 @@ class Controller:
1066
1146
  significant bit is pin 0 on the respective port.
1067
1147
  """
1068
1148
 
1069
- result = self._make_command(query=query)
1149
+ result = self._make_command(
1150
+ query=query, query_override=query_override)
1070
1151
 
1071
1152
  data_buf = io.BytesIO()
1072
1153
  writer = Writer(data_buf)
@@ -1082,8 +1163,7 @@ class Controller:
1082
1163
  if combiner.maybe_write():
1083
1164
  writer.write_int8(aux2)
1084
1165
 
1085
- if query:
1086
- data_buf.write(self._query_data)
1166
+ self._format_query(query, query_override, data_buf, result)
1087
1167
 
1088
1168
  result.data = data_buf.getvalue()
1089
1169
  return result
@@ -1157,6 +1237,7 @@ class Controller:
1157
1237
  result.parse = make_diagnostic_parser(self.id, channel)
1158
1238
 
1159
1239
  result.data = data_buf.getvalue()
1240
+ result.expected_reply_size = 3 + max_length
1160
1241
  return result
1161
1242
 
1162
1243
  async def diagnostic_read(self, *args, **kwargs):
moteus/moteus_tool.py CHANGED
@@ -818,7 +818,8 @@ class Stream:
818
818
  await self.check_for_fault()
819
819
 
820
820
  enc_kp, enc_ki, enc_bw_hz = await self.set_encoder_filter(
821
- torque_bw_hz, control_rate_hz=control_rate_hz)
821
+ torque_bw_hz, inductance,
822
+ control_rate_hz=control_rate_hz)
822
823
  await self.check_for_fault()
823
824
 
824
825
  v_per_hz = await self.calibrate_kv_rating(
@@ -1163,7 +1164,7 @@ class Stream:
1163
1164
  print(f"Calculated inductance: {inductance}H")
1164
1165
  return inductance
1165
1166
 
1166
- async def set_encoder_filter(self, torque_bw_hz, control_rate_hz = None):
1167
+ async def set_encoder_filter(self, torque_bw_hz, inductance, control_rate_hz = None):
1167
1168
  # Check to see if our firmware supports encoder filtering.
1168
1169
  motor_position_style = await self.is_config_supported("motor_position.sources.0.pll_filter_hz")
1169
1170
  servo_style = await self.is_config_supported("servo.encoder_filter.enabled")
@@ -1173,18 +1174,17 @@ class Stream:
1173
1174
  if self.args.encoder_bw_hz:
1174
1175
  desired_encoder_bw_hz = self.args.encoder_bw_hz
1175
1176
  else:
1176
- if self.args.cal_hall:
1177
- # Hall effect configurations require a low encoder BW
1178
- # if the hall effects are also used for position and
1179
- # velocity control. Since that is one of the most
1180
- # common ways of using hall effects, we by default cap
1181
- # the bw at 20Hz and use a lower one if the torque bw
1182
- # would otherwise have been lower.
1183
- desired_encoder_bw_hz = min(20, 2 * torque_bw_hz)
1184
- else:
1185
- # We default to an encoder bandwidth of 50Hz, or 2x the
1186
- # torque bw, whichever is larger.
1187
- desired_encoder_bw_hz = max(50, 2 * torque_bw_hz)
1177
+ # Don't let the encoder bandwidth be less than 10Hz by default.
1178
+ desired_encoder_bw_hz = max(10, 2 * torque_bw_hz)
1179
+
1180
+ # However, we limit the maximum encoder bandwidth for high
1181
+ # inductance motors, because they shouldn't be able to
1182
+ # accelerate that fast anyways.
1183
+ if inductance:
1184
+ # This is just a random constant that seems to work
1185
+ # well in practice.
1186
+ desired_encoder_bw_hz = min(
1187
+ desired_encoder_bw_hz, 2e-2 / inductance)
1188
1188
 
1189
1189
  # And our bandwidth with the filter can be no larger than
1190
1190
  # 1/10th the control rate.
@@ -1203,7 +1203,8 @@ class Stream:
1203
1203
  await self.command(f"conf set servo.encoder_filter.kp {kp}")
1204
1204
  await self.command(f"conf set servo.encoder_filter.ki {ki}")
1205
1205
  elif motor_position_style:
1206
- await self.command(f"conf set motor_position.sources.0.pll_filter_hz {encoder_bw_hz}")
1206
+ commutation_source = await self.read_config_int("motor_position.commutation_source")
1207
+ await self.command(f"conf set motor_position.sources.{commutation_source}.pll_filter_hz {encoder_bw_hz}")
1207
1208
  else:
1208
1209
  assert False
1209
1210
  return kp, ki, encoder_bw_hz
moteus/multiplex.py CHANGED
@@ -244,21 +244,30 @@ class WriteFrame:
244
244
 
245
245
  def __init__(self, buf):
246
246
  self._buf = buf
247
+ self._size = 0
248
+
249
+ def size(self):
250
+ return self._size
247
251
 
248
252
  def write(self, value, resolution):
249
253
  self._buf.write(TYPES[resolution].pack(value))
254
+ self._size += TYPES[resolution].size
250
255
 
251
256
  def write_int8(self, value):
252
257
  self._buf.write(TYPES[INT8].pack(value))
258
+ self._size += 1
253
259
 
254
260
  def write_int16(self, value):
255
261
  self._buf.write(TYPES[INT16].pack(value))
262
+ self._size += 2
256
263
 
257
264
  def write_int32(self, value):
258
265
  self._buf.write(TYPES[INT32].pack(value))
266
+ self._size += 4
259
267
 
260
268
  def write_f32(self, value):
261
269
  self._buf.write(TYPES[F32].pack(value))
270
+ self._size += 4
262
271
 
263
272
  def write_varuint(self, value):
264
273
  while True:
@@ -266,6 +275,7 @@ class WriteFrame:
266
275
  value >>= 7
267
276
  this_byte |= 0x80 if value else 0x00
268
277
  self._buf.write(bytes([this_byte]))
278
+ self._size += 1
269
279
 
270
280
  if value == 0:
271
281
  break
@@ -286,6 +296,7 @@ class WriteCombiner:
286
296
  self.base_command = base_command
287
297
  self.start_register = start_register
288
298
  self.resolutions = resolutions
299
+ self.reply_size = 0
289
300
  self._offset = 0
290
301
  self._current_resolution = -1
291
302
 
@@ -320,6 +331,8 @@ class WriteCombiner:
320
331
 
321
332
  write_command = [0x00, 0x04, 0x08, 0x0c][new_resolution] | self.base_command
322
333
 
334
+ start_size = self.writer.size()
335
+
323
336
  if count <= 3:
324
337
  # Use the shorthand formulation.
325
338
  self.writer.write_int8(write_command + count)
@@ -329,4 +342,10 @@ class WriteCombiner:
329
342
  self.writer.write_int8(count)
330
343
 
331
344
  self.writer.write_varuint(self.start_register + this_offset)
345
+
346
+ end_size = self.writer.size()
347
+
348
+ self.reply_size += end_size - start_size
349
+ self.reply_size += count * resolution_size(new_resolution)
350
+
332
351
  return True
moteus/pythoncan.py CHANGED
@@ -87,10 +87,13 @@ class PythonCan:
87
87
  arbitration_id = (command.destination |
88
88
  (0x8000 if reply_required else 0) |
89
89
  (command.can_prefix << 16))
90
+ on_wire_size = self._round_up_dlc(len(command.data))
91
+ full_message = (command.data +
92
+ bytes([0x50]) * (on_wire_size - len(command.data)))
90
93
  message = can.Message(arbitration_id=arbitration_id,
91
94
  is_extended_id=(arbitration_id >= 0x7ff),
92
- dlc=len(command.data),
93
- data=bytearray(command.data),
95
+ dlc=on_wire_size,
96
+ data=full_message,
94
97
  is_fd=True,
95
98
  bitrate_switch=not self._disable_brs)
96
99
 
@@ -99,3 +102,22 @@ class PythonCan:
99
102
  async def read(self):
100
103
  self._maybe_setup()
101
104
  return await self._reader.get_message()
105
+
106
+ def _round_up_dlc(self, size):
107
+ if size <= 8:
108
+ return size
109
+ if size <= 12:
110
+ return 12
111
+ if size <= 16:
112
+ return 16
113
+ if size <= 20:
114
+ return 20
115
+ if size <= 24:
116
+ return 24
117
+ if size <= 32:
118
+ return 32
119
+ if size <= 48:
120
+ return 48
121
+ if size <= 64:
122
+ return 64
123
+ return size
moteus/version.py CHANGED
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- VERSION="0.3.57"
15
+ VERSION="0.3.59"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: moteus
3
- Version: 0.3.57
3
+ Version: 0.3.59
4
4
  Summary: moteus brushless controller library and tools
5
5
  Home-page: https://github.com/mjbots/moteus
6
6
  Author: mjbots Robotic Systems
@@ -2,22 +2,22 @@ moteus/__init__.py,sha256=1Prmih65X7Qz7WY8AUiSwUWXz3Dev8ggQS9xohL75M0,721
2
2
  moteus/aioserial.py,sha256=lOBFPXManRBMsjMc1zuyw-NRt6CNVMvnj_mQqIZLpyI,1061
3
3
  moteus/aiostream.py,sha256=SLDXwdBMHIWVTAmeUGG6ke7sntfNVxd9CqapzfniJOo,3678
4
4
  moteus/calibrate_encoder.py,sha256=kY6mV1WkgCrD9z2YpRoqTTK_BUrRfop4_YJym9AQI-I,12403
5
- moteus/command.py,sha256=YEtDNXX-TlkUSElsGVQoSYbGqcboneNEejZo06v8ZsI,1128
5
+ moteus/command.py,sha256=IXb0ToAg74fm8FYKPiOdjlzxosqdafLhqADffCvw5OY,1156
6
6
  moteus/export.py,sha256=dI8QjdqrcI3pi5fKfP25PwLvsIlVJ695x0ta0PVMKx8,1628
7
- moteus/fdcanusb.py,sha256=AMwqdvIRz4X9yA4qz8REuyFBfs3OcLcVqbGX28sEx4s,6729
8
- moteus/moteus.py,sha256=W-ADEI1uAHF0j5A3s1CINTUYpwjEgO-xPRQS15C9Ewg,43410
9
- moteus/moteus_tool.py,sha256=WSJH5kRRF3SSsocGR_TFbRwAeGP0YlexMpes9iS_rt0,66794
10
- moteus/multiplex.py,sha256=0Hs63pFZpl1y7ciTcwEeg8F2JDjlhrkb2FcfUbdox-g,9637
7
+ moteus/fdcanusb.py,sha256=6nfnqxwJqyqX0MOn7pj3-kqWq-Vzwx5M8I0wUux3RnA,7294
8
+ moteus/moteus.py,sha256=7lObHdVYCELclm6JMg-QygbAmRgNyQrS0BxkOjERJlo,46993
9
+ moteus/moteus_tool.py,sha256=L-7IBs9YHcbd2nc8SPwCEinHvzp09iVb_JeQCUtNM2U,66800
10
+ moteus/multiplex.py,sha256=EBOhAE-DHkS_AXtqUl2YEs745evvLMPIMeXQSYxF8zk,10102
11
11
  moteus/posix_aioserial.py,sha256=3JFiY5p4dtC2ztg6N5SOffnNk9lNcjie02yjD3UlJWo,2971
12
- moteus/pythoncan.py,sha256=lawewmkd9zQuE-Z1LurmpFD2iSWATei65SZb42um_Vg,3309
12
+ moteus/pythoncan.py,sha256=629d_DUfunyXpMZcaA6ICuvgodes-KH7OVDC7smco7s,3886
13
13
  moteus/reader.py,sha256=jGADQTmONSBMQ25I5AqXELLqil2TEha1KjraPdOsf78,11932
14
14
  moteus/regression.py,sha256=wpPlxAZ9nC9kfv0oX1K9W2AZNnBLbY8htAJz60SxIb8,1183
15
15
  moteus/router.py,sha256=k4Tf6hWtHSgzznmdnj4NySe84-y9feYRxGz0yTrJtoc,2043
16
16
  moteus/transport.py,sha256=3asI2A87eQDImLP74LNLtETaShQRiD9RuCjlxNY6AlE,1003
17
- moteus/version.py,sha256=MFTT8rAzrh1zN6v98RZRdgK04yThpVW45c1KwhhP0SU,609
17
+ moteus/version.py,sha256=lYdyuBwllEdH5WL5gQPVzkIpGpTIZtY-1x9F1WUWUa8,609
18
18
  moteus/win32_aioserial.py,sha256=SZsnoBWE0Uwo4ZZF8ALB1WNPRY9NiaCOBz6VfvVcnxA,1841
19
- moteus-0.3.57.dist-info/METADATA,sha256=HNeottaB3MbXGZcq5Jn_knhHxdKt7I5o-93DKOwOivI,3417
20
- moteus-0.3.57.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
21
- moteus-0.3.57.dist-info/entry_points.txt,sha256=indCsEML1fmtWJU1WiV-d7UmmTaAMhyBLEc1iiKnexQ,57
22
- moteus-0.3.57.dist-info/top_level.txt,sha256=aZzmI_yecTaDrdSp29pTJuowaSQ9dlIZheQpshGg4YQ,7
23
- moteus-0.3.57.dist-info/RECORD,,
19
+ moteus-0.3.59.dist-info/METADATA,sha256=Wye8gTlX1__CYw-VJcnfC7w7ynANVPsjOkcGNtxGgOU,3417
20
+ moteus-0.3.59.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
21
+ moteus-0.3.59.dist-info/entry_points.txt,sha256=indCsEML1fmtWJU1WiV-d7UmmTaAMhyBLEc1iiKnexQ,57
22
+ moteus-0.3.59.dist-info/top_level.txt,sha256=aZzmI_yecTaDrdSp29pTJuowaSQ9dlIZheQpshGg4YQ,7
23
+ moteus-0.3.59.dist-info/RECORD,,