End Effector Instructions
+Dexter's Joint 6 is typically a "twist wrist". Joint 7 is typically a gripper.
+The motors for these joints can be tweaked using the Servo class.
+But for the common gross motions, use the below Job instructions.
+
+ Like most Dexter instructions, you can prefix these with "Dexter." to use the
+ robot of the Job, or "Dexter.dexter0." to send the instructions to a particular Dexter.
+
+ move_until_torque
+ Causes joint 6 or 7 to move until it reaches a certain torque, at which point motion stops.
+ Parameters:
+ goal_degrees A number. Default: 90
+ joint_number 6 or 7. Default: 7
+ torque_limit A number. Default: 500
+ Example:
+new Job({name: "move_ee",
+ do_list: [Dexter.move_until_torque(45, 6, 450)]
+})
+
+
+ move_until_static
+ Like move_until_torque but doesn't take a torque argument.
+ This instruction stops trying to move the joint when the joint is blocked from moving.
+ If joint 6 or 7 is blocked from moving, it will overheat if it continues to try to
+ move for several seconds. This can happen when j7 gripper is closed as it can be,
+ or joint 6 is trying to twist a bolt that's frozen.
+ This is often more convenient to use than move_until_torque.
+ Parameters:
+ goal_degrees A number. Default: 20
+ joint_number A number. Default: 7
+ degree_tolerance A number. Default: 0.01
+ Example:
+new Job({name: "move_ee",
+ do_list: [Dexter.move_until_static(100, 7)]
+})
+
+
+ twist
+ Twist the wrist (joint 6).
+ Parameter:
+ goal_degrees A number. Default: 0
+ Range Dexter.J6_ANGLE_MIN
to Dexter.J6_ANGLE_MAX
+ Example:
+new Job({name: "move_ee",
+ do_list: [Dexter.twist(45)]
+})
+
+
+ grasp
+ Close the gripper (Joint 7) until it can't move due to gripping an object,
+ or reaches 20 degrees, whichever occurs first.
+ Parameter:
+ min_degrees A number. Default: 20
+ Example:
+new Job({name: "move_ee",
+ do_list: [Dexter.grasp()]
+})
+
+ ungrasp
+ Open the gripper (Joint 7) until it can't move due to touching something
+ or reaches max_degrees. whichever occurs first.
+ Parameter:
+ max_degrees Default: 270
+ The theoretical max_degrees is 296 but without perfect calibration,
+ its best to set it lower.
+ Example:
+new Job({name: "move_ee",
+ do_list: [Dexter.ungrasp()]
+})
+
+
+
+ Servo
+ Dexter's, as of March 2024, typically have 7 joints, each with its own motor.
+ Joints 6 and 7 are different than the others. The Servo class helps
+ control the fine points of these motors.
+ For more typical control via Job instructions, see
+ End Effector Instructions
+
+
+ Terminology
+ servo_firmware_type A positive integer representing the low level type of a motor on Dexter
+ Examples:
+
+ - 1 (nothing: doesn't exist)
+ - 8 (steppers, joint 1 thru 5)
+ - 320 (possibly for j6 & j7)
+ - 430 (possibly for j6 & j7)
+
+ servo_model_class_name A string. Examples: "XL320", "XL330", "XC430" "XC430_LOAD"
+ servo_model_number An Integer. Examples: 350, 1200, 1240, 1080
+ servo_model_name A string. Examples: "XL320", "XL330-M288-T", "XC330-M288-T" "XC430-W240-T" (one to one with servo_model_number)
+ servo_id An integer designating a servo on a Dexter.
+ Examples: 3 for J6, 1 for j7, (j1 thru 5 not in this category because are not servos, they are steppers)
+
+ Data
+ Servo.models
Name-value pairs where the name is a servo_model_class_name
+ and the value is lots of data about that model class. Use DDE's inspector to see it.
+ Servo.default_servos
An array of actual servos data for testing purposes.
+ The indexes of the array are servo_id's.
+ servo_status A json object containing the status of a servo on a Dexter.
+ Used in a_job.user_data.servo_status
+
+ Methods
+ Servo.set_servo_id_model Set values in a servo object.
+ Parameters:
+ servo_id
+ model_or_servo_model_class_name
+ robot
+
+ Servo.servo_id_to_servo Returns the servo data.
+ Parameters:
+ servo_id
+ a_dexter
+
+ Servo.servo_property Return the value of property_name in the servo data table.
+ Parameters:
+ servo The servo data.
+ property_name A string.
+
+
+ Example uses in a Job
+
+new Job({ //test
+ name: "test_servos", user_data: {count: 0},
+ do_list: [ IO.out("starting at "+Date())
+ //,Dexter.read_from_robot("#Servo 3 0 2", "servo_status")
+ //,function() { out("servo returned:"+this.user_data.servo_status+" good luck with that") }
+ ,Dexter.servo_get(3, "MODEL") //instead of the above, just do this
+ ,function() {
+ let servo = this.user_data.servo_status;
+ out(servo.id+" is a "+servo.type);
+ out(Servo.servo_id_to_servo(3, this) === this.robot.servos[3]) //test that we did learn it
+ }
+ //,Dexter.dexter1.servo_get(3, "MODEL")() //as expected, fails; there is no dexter1 robot. move to doc example for servo-get
+ //,Dexter.servo_get(2, "MODEL") //as expected, fails; there is no servo id 2. ,,, move as above
+ ,Dexter.servo_detect(1) //just calls servo_get(id, "MODEL"), if found, servo is added to the robot.
+ //,"S ServoSetX 1, 116, 12, %01%00%00%00" //only if it's a 430 ,, move to doc for servp_set
+ //,"S ServoSetX 1, 30, 6, %01%00" //only if it's a 320
+ //,"S ServoSet2X 1, 30, 1" //only if it's a 320, can't support a 430's 4 byte position
+ ,Dexter.servo_set(1, "GOAL_POSITION", 400)
+ ,Dexter.servo_set(Dexter.dexter0.servos.roll, "TORQUE", 0) //must specify robot to use named servos. sigh.
+
+ ,function() {inspect(this.user_data.servo_status)}
+ ]
+})
+
+