Improve CLI
This commit is contained in:
parent
583f5e2933
commit
d3691b10db
10 changed files with 72 additions and 58 deletions
12
README.md
12
README.md
|
@ -11,9 +11,9 @@ In root directory, call `make` to generate binary file. To clean up, call `make
|
||||||
```bash
|
```bash
|
||||||
./motorsim -c ./cfg/motors.yaml motor --id [ID] serve
|
./motorsim -c ./cfg/motors.yaml motor --id [ID] serve
|
||||||
```
|
```
|
||||||
2. Start Motor controller server (Beware, controllers commands except Port 8080):
|
2. Start Motor controller server (Beware, if modifying port controllers commands except Port 8080):
|
||||||
```bash
|
```bash
|
||||||
./motorsim -c ./cfg/motors.yaml controller serve -p 8080
|
./motorsim -c ./cfg/motors.yaml controller serve
|
||||||
```
|
```
|
||||||
### Client usage
|
### Client usage
|
||||||
#### Motor controller commands
|
#### Motor controller commands
|
||||||
|
@ -21,9 +21,9 @@ In root directory, call `make` to generate binary file. To clean up, call `make
|
||||||
```bash
|
```bash
|
||||||
./motorsim -c ./cfg/motors.yaml controller getJoints
|
./motorsim -c ./cfg/motors.yaml controller getJoints
|
||||||
```
|
```
|
||||||
- Set joints values (Beware, currently set the same values for all joints):
|
- Set joints values:
|
||||||
```bash
|
```bash
|
||||||
./motorsim -c ./cfg/motors.yaml controller setJoints --j [Joint goals]
|
./motorsim -c ./cfg/motors.yaml controller setJoints [Joint goals]
|
||||||
```
|
```
|
||||||
#### Motor commands
|
#### Motor commands
|
||||||
Every command should be provided with valid ID from [config file](./cfg/motors.yaml):
|
Every command should be provided with valid ID from [config file](./cfg/motors.yaml):
|
||||||
|
@ -33,7 +33,7 @@ Every command should be provided with valid ID from [config file](./cfg/motors.y
|
||||||
```
|
```
|
||||||
- Set motor velocity:
|
- Set motor velocity:
|
||||||
```bash
|
```bash
|
||||||
./motorsim -c ./cfg/motors.yaml motor --id [ID] moveVel --vel [Velocity]
|
./motorsim -c ./cfg/motors.yaml motor --id [ID] moveVel -- [Velocity]
|
||||||
```
|
```
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
@ -42,4 +42,4 @@ Every command should be provided with valid ID from [config file](./cfg/motors.y
|
||||||
- [ ] Use coroutines for gRPC services callbacks.
|
- [ ] Use coroutines for gRPC services callbacks.
|
||||||
- [ ] Fix get Motor State service (Behavior not robust).
|
- [ ] Fix get Motor State service (Behavior not robust).
|
||||||
- [ ] Fix Controller SetJoint command CLI omitting multiples input values.
|
- [ ] Fix Controller SetJoint command CLI omitting multiples input values.
|
||||||
- [ ] Complete CLI help messages.
|
- [ ] Send error responses to clients of gRPC services.
|
|
@ -23,10 +23,10 @@ var(
|
||||||
// controllerCmd represents the controller command
|
// controllerCmd represents the controller command
|
||||||
var controllerCmd = &cobra.Command{
|
var controllerCmd = &cobra.Command{
|
||||||
Use: "controller",
|
Use: "controller",
|
||||||
Short: "controller command description",
|
Short: "Motor controller command",
|
||||||
Long: `controller command description`,
|
Long: `Motor controller command`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("controller called")
|
cmd.Help() //Nothing to do, show help
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,9 @@ import (
|
||||||
// getJointsCmd represents the getJoints command
|
// getJointsCmd represents the getJoints command
|
||||||
var getJointsCmd = &cobra.Command{
|
var getJointsCmd = &cobra.Command{
|
||||||
Use: "getJoints",
|
Use: "getJoints",
|
||||||
Short: "getJoints command descritpion",
|
Short: "Retreive motor positions",
|
||||||
Long: `getJoints command descritpion`,
|
Long: `Retreive the positions of the motor controlled by Motor Controller server`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
// fmt.Println("getJoints called")
|
|
||||||
GetJoints()
|
GetJoints()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ var (
|
||||||
// serveCmd represents the serve command
|
// serveCmd represents the serve command
|
||||||
var serveCmd = &cobra.Command{
|
var serveCmd = &cobra.Command{
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
Short: "serve command descritpion",
|
Short: "Start Motor Controller server",
|
||||||
Long: `serve command descritpion`,
|
Long: `Start Motor Controller server`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("serve called with: Port ", port)
|
fmt.Println("serve called with: Port ", port)
|
||||||
fmt.Println(cmd.CommandPath())
|
fmt.Println(cmd.CommandPath())
|
||||||
|
@ -66,7 +66,7 @@ type server struct {
|
||||||
func (s *server) SetJoints(ctx context.Context, in *pb.Angles) (*pb.Angles, error) {
|
func (s *server) SetJoints(ctx context.Context, in *pb.Angles) (*pb.Angles, error) {
|
||||||
//TODO: use as a coroutine to prevent blocking the main thread
|
//TODO: use as a coroutine to prevent blocking the main thread
|
||||||
var(
|
var(
|
||||||
tgt_angles = []float64{in.GetAngles()[0], in.GetAngles()[0], in.GetAngles()[0]} //TODO: Replace by in.GetAngles()
|
tgt_angles = in.GetAngles() //TODO: Replace by in.GetAngles()
|
||||||
cmd_vel = make([]float64, len(tgt_angles)) //Commanded velocities
|
cmd_vel = make([]float64, len(tgt_angles)) //Commanded velocities
|
||||||
motor_pos = []float64{} //Current motor positions
|
motor_pos = []float64{} //Current motor positions
|
||||||
max_vels = []float64{} //Maximum velocities
|
max_vels = []float64{} //Maximum velocities
|
||||||
|
@ -78,7 +78,7 @@ func (s *server) SetJoints(ctx context.Context, in *pb.Angles) (*pb.Angles, erro
|
||||||
)
|
)
|
||||||
|
|
||||||
//Get motor states
|
//Get motor states
|
||||||
for i := range tgt_angles {
|
for i := range motor_configs {
|
||||||
//TODO: Use a coroutine to avoid blocking the main thread
|
//TODO: Use a coroutine to avoid blocking the main thread
|
||||||
motor_state := getMotorState(i) //Get current motor state
|
motor_state := getMotorState(i) //Get current motor state
|
||||||
motor_pos = append(motor_pos, motor_state.Angle)
|
motor_pos = append(motor_pos, motor_state.Angle)
|
||||||
|
@ -92,6 +92,9 @@ func (s *server) SetJoints(ctx context.Context, in *pb.Angles) (*pb.Angles, erro
|
||||||
// log.Printf("t: %v - %v", slices.Min(traj_times), traj_times)
|
// log.Printf("t: %v - %v", slices.Min(traj_times), traj_times)
|
||||||
|
|
||||||
if controller_error ==""{ //If no error, check for valid target angles
|
if controller_error ==""{ //If no error, check for valid target angles
|
||||||
|
if len(tgt_angles)> len(motor_configs){ //Check size
|
||||||
|
controller_error = "InvalidRequestSize"
|
||||||
|
} else { //Check angles limit
|
||||||
for i, angle := range tgt_angles {
|
for i, angle := range tgt_angles {
|
||||||
if angle > motor_configs[i].Max_pos || angle < motor_configs[i].Min_pos{
|
if angle > motor_configs[i].Max_pos || angle < motor_configs[i].Min_pos{
|
||||||
controller_error = "InvalidTargetAngles"
|
controller_error = "InvalidTargetAngles"
|
||||||
|
@ -99,6 +102,7 @@ func (s *server) SetJoints(ctx context.Context, in *pb.Angles) (*pb.Angles, erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Compute velocities
|
//Compute velocities
|
||||||
if controller_error ==""{ //If no error, compute velocities
|
if controller_error ==""{ //If no error, compute velocities
|
||||||
|
@ -136,6 +140,9 @@ func (s *server) SetJoints(ctx context.Context, in *pb.Angles) (*pb.Angles, erro
|
||||||
case "MotorFault":
|
case "MotorFault":
|
||||||
log.Printf("ERROR - Motor error preventing command: %v", motor_errors)
|
log.Printf("ERROR - Motor error preventing command: %v", motor_errors)
|
||||||
return &pb.Angles{Angles: motor_pos}, nil //Return motor positions TODO: Return error message
|
return &pb.Angles{Angles: motor_pos}, nil //Return motor positions TODO: Return error message
|
||||||
|
case "InvalidRequestSize":
|
||||||
|
log.Printf("ERROR - Request size [%d] is invalid. Expected size [%d]", len(tgt_angles), len(motor_configs))
|
||||||
|
return &pb.Angles{Angles: motor_pos}, nil
|
||||||
case "InvalidTargetAngles":
|
case "InvalidTargetAngles":
|
||||||
log.Print("ERROR - out of limits:")
|
log.Print("ERROR - out of limits:")
|
||||||
for i, tgt := range tgt_angles{
|
for i, tgt := range tgt_angles{
|
||||||
|
@ -191,7 +198,7 @@ func setMotorVel(idx int, vel float64){
|
||||||
|
|
||||||
//TODO: Fix compiling issue with google.protobuf.Empty message
|
//TODO: Fix compiling issue with google.protobuf.Empty message
|
||||||
func (s *server) GetJoints(ctx context.Context, in *pb.Empty) (*pb.Angles, error) {
|
func (s *server) GetJoints(ctx context.Context, in *pb.Empty) (*pb.Angles, error) {
|
||||||
//TODO: use a coroutines to prevent blocking the main thread
|
//TODO: use a coroutines to prevent blocking the main thread and server crash in case of errors
|
||||||
var angles = []float64{}
|
var angles = []float64{}
|
||||||
for i, _ := range motor_configs {
|
for i, _ := range motor_configs {
|
||||||
angles = append(angles,getMotorState(i).Angle)
|
angles = append(angles,getMotorState(i).Angle)
|
||||||
|
|
|
@ -6,7 +6,7 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
|
@ -22,16 +22,21 @@ import (
|
||||||
// setJointsCmd represents the setJoints command
|
// setJointsCmd represents the setJoints command
|
||||||
var setJointsCmd = &cobra.Command{
|
var setJointsCmd = &cobra.Command{
|
||||||
Use: "setJoints",
|
Use: "setJoints",
|
||||||
Short: "setJoints command descritpion",
|
Short: "Set target joint angles for the motors",
|
||||||
Long: `setJoints command descritpion`,
|
Long: `Request the Motor Controller to command the motors to reach the target joint angles`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("setJoints called with target :", cmd.Flag("j").Value)
|
//Parse target angles
|
||||||
tgt_angles, err:=cmd.Flags().GetFloat64Slice("j")
|
var tgt_angles []float64
|
||||||
|
for _, arg := range args {
|
||||||
|
angle, err := strconv.ParseFloat(arg, 64)
|
||||||
if err!= nil{
|
if err!= nil{
|
||||||
log.Fatalf("Failed read requested velocities %v", err)
|
log.Fatalf("Failed to parse joint angle %v", err)
|
||||||
}else{
|
}else{
|
||||||
SetJoints(tgt_angles)
|
tgt_angles = append(tgt_angles, angle)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
//Set joint angles
|
||||||
|
SetJoints(tgt_angles)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,10 +51,8 @@ func init() {
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
// Cobra supports local flags which will only run when this command
|
||||||
// is called directly, e.g.:
|
// is called directly, e.g.:
|
||||||
//TODO: Anonymous flag (with anonymous flag group ?)
|
setJointsCmd.Flags().Float64Slice("", nil, "Target joint values") //Only used for help message
|
||||||
//TODO: Fix input of multiple values as slice
|
// setJointsCmd.MarkFlagRequired("")
|
||||||
setJointsCmd.Flags().Float64Slice("j", nil, "Target joint values") //or IntSlice ?
|
|
||||||
setJointsCmd.MarkFlagRequired("j")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//gRPC Client
|
//gRPC Client
|
||||||
|
@ -70,5 +73,5 @@ func SetJoints(tgt_angles []float64){
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not send: %v", err)
|
log.Fatalf("could not send: %v", err)
|
||||||
}
|
}
|
||||||
log.Printf("Received: %v", r.GetAngles())
|
log.Printf("Moving to (°): %v", r.GetAngles())
|
||||||
}
|
}
|
|
@ -22,10 +22,10 @@ import (
|
||||||
// listenCmd represents the listen command
|
// listenCmd represents the listen command
|
||||||
var listenCmd = &cobra.Command{
|
var listenCmd = &cobra.Command{
|
||||||
Use: "listen",
|
Use: "listen",
|
||||||
Short: "listen command descritpion",
|
Short: "Listen motor state",
|
||||||
Long: `listen command descritpion`,
|
Long: `Continuous retrieval of complete motor state from motor server`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("listen called with ID: ", cmd.Flag("id").Value)
|
// fmt.Println("listen called with ID: ", cmd.Flag("id").Value)
|
||||||
updateConfig()
|
updateConfig()
|
||||||
listen() //Blocking call
|
listen() //Blocking call
|
||||||
},
|
},
|
||||||
|
|
|
@ -39,10 +39,10 @@ var(
|
||||||
// motorCmd represents the motor command
|
// motorCmd represents the motor command
|
||||||
var motorCmd = &cobra.Command{
|
var motorCmd = &cobra.Command{
|
||||||
Use: "motor",
|
Use: "motor",
|
||||||
Short: "motor command descritpion",
|
Short: "Motor command",
|
||||||
Long: `motor command descritpion`,
|
Long: `Motor command`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("motor called with ID: ", cmd.Flag("id").Value)
|
cmd.Help() //Nothing to do, show help
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ func init() {
|
||||||
// Cobra supports Persistent Flags which will work for this command
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
// and all subcommands, e.g.:
|
// and all subcommands, e.g.:
|
||||||
motorCmd.PersistentFlags().Uint16Var(&motorID, "id", 0, "Identifier number")
|
motorCmd.PersistentFlags().Uint16Var(&motorID, "id", 0, "Identifier number")
|
||||||
motorCmd.MarkFlagRequired("id")
|
motorCmd.MarkPersistentFlagRequired("id")
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
// Cobra supports local flags which will only run when this command
|
||||||
// is called directly, e.g.:
|
// is called directly, e.g.:
|
||||||
|
|
|
@ -6,6 +6,7 @@ package motor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
@ -22,16 +23,20 @@ import (
|
||||||
// moveVelCmd represents the moveVel command
|
// moveVelCmd represents the moveVel command
|
||||||
var moveVelCmd = &cobra.Command{
|
var moveVelCmd = &cobra.Command{
|
||||||
Use: "moveVel",
|
Use: "moveVel",
|
||||||
Short: "moveVel command descritpion",
|
Short: "Set velocity of motor",
|
||||||
Long: `moveVel command descritpion`,
|
Long: `Request motor server to move at given velocity`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("moveVel called with: ID ", cmd.Flag("id").Value, " Vel ", cmd.Flag("vel").Value)
|
//Parse target velocity
|
||||||
updateConfig()
|
if len(args)!=1 {
|
||||||
var vel, err = cmd.Flags().GetFloat64("vel")
|
log.Fatalf("Wrong number of arguments")
|
||||||
if err!= nil {
|
|
||||||
log.Fatalf("Failed read requested velocity %v", err)
|
|
||||||
}else{
|
}else{
|
||||||
moveVel(vel)
|
vel, err := strconv.ParseFloat(args[0],64)
|
||||||
|
if err!= nil{
|
||||||
|
log.Fatalf("Failed to parse joint velocity %v", err)
|
||||||
|
} else {
|
||||||
|
updateConfig()
|
||||||
|
moveVel(vel) //Set motor velocity
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -47,8 +52,8 @@ func init() {
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
// Cobra supports local flags which will only run when this command
|
||||||
// is called directly, e.g.:
|
// is called directly, e.g.:
|
||||||
//TODO: Anonymous flag (with anonymous flag group ?)
|
moveVelCmd.Flags().Float64("", 0, "Velocity (degrees/s)") //Only used for help message
|
||||||
moveVelCmd.Flags().Float64("vel", 0, "Velocity (degrees/s)")
|
// moveVelCmd.MarkFlagRequired("vel")
|
||||||
}
|
}
|
||||||
|
|
||||||
//gRPC Client
|
//gRPC Client
|
||||||
|
@ -68,5 +73,7 @@ func moveVel(cmd_vel float64){
|
||||||
_, err = c.SetVelocity(ctx, &pb.Velocity{Velocity: cmd_vel})
|
_, err = c.SetVelocity(ctx, &pb.Velocity{Velocity: cmd_vel})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not send: %v", err)
|
log.Fatalf("could not send: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Requested velocity [%f]°/s to motor %d", cmd_vel, curr_config.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,11 +27,9 @@ var(
|
||||||
// serveCmd represents the serve command
|
// serveCmd represents the serve command
|
||||||
var serveCmd = &cobra.Command{
|
var serveCmd = &cobra.Command{
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
Short: "serve command descritpion",
|
Short: "Start motor server",
|
||||||
Long: `serve command descritpion`,
|
Long: `Start motor server`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("serve called with ID ", cmd.Flag("id").Value)
|
|
||||||
// fmt.Println(cmd.CommandPath())
|
|
||||||
updateConfig()
|
updateConfig()
|
||||||
init_sim()
|
init_sim()
|
||||||
serve()
|
serve()
|
||||||
|
|
|
@ -42,12 +42,12 @@ func init() {
|
||||||
// will be global for your application.
|
// will be global for your application.
|
||||||
|
|
||||||
RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.cobra.yaml)")
|
RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.cobra.yaml)")
|
||||||
//RootCmd.MarkFlagRequired("config")
|
RootCmd.MarkPersistentFlagRequired("config")
|
||||||
// viper.BindPFlag("config", RootCmd.PersistentFlags().Lookup("config"))
|
// viper.BindPFlag("config", RootCmd.PersistentFlags().Lookup("config"))
|
||||||
|
|
||||||
// Cobra also supports local flags, which will only run
|
// Cobra also supports local flags, which will only run
|
||||||
// when this action is called directly.
|
// when this action is called directly.
|
||||||
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
// RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
}
|
}
|
||||||
|
|
||||||
// initConfig reads in config file and ENV variables if set.
|
// initConfig reads in config file and ENV variables if set.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue