Skip to content

Commit

Permalink
Merge pull request #2 from mainflux/refactor
Browse files Browse the repository at this point in the history
NOISSUE - Refactor CoAP CLI tool
  • Loading branch information
dborovcanin authored Oct 13, 2020
2 parents 43f8edd + b7841ed commit 2e65f08
Show file tree
Hide file tree
Showing 757 changed files with 213,601 additions and 1,285 deletions.
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@ Simple CoAP cli client written in Go.


## Usage
Pre-built binary can be found here: https://github.com/mainflux/coap-cli/releases/tag/v0.1.0.
Pre-built binary can be found here: https://github.com/mainflux/coap-cli/releases/tag/v0.2.0.
When running, please provide following format:
`coap-cli` followed by method code (`get`, `put`, `post`, `delete`) and CoAP URL. After that, you can pass following flags:

| Flag | Description | Default value |
|-------|--------------------------------|----------------------------------------|
| ACK | Acknowledgement | false |
| C | Confirmable | false |
| NC | Non-Confirmable | false |
| O | Observe | false |
| d | Data to be sent in POST or PUT | "" |
| id | Message ID | 0 |
| token | Token | Byte array of empty string: [49 50 51] |
| Flag | Description | Default value |
| ---- | ---------------------------------------------- | ------------- |
| o | Observe option - only valid with Get request | false |
| auth | Auth option sent as URI Query | "" |
| h | Host | "localhost" |
| p | port | "5683" |
| d | Data to be sent in POST or PUT | "" |
| cf | Content format | 50 - JSON |
# Examples:

```bash
coap-cli get coap://localhost/channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic\?authorization=1e1017e6-dee7-45b4-8a13-00e6afeb66eb -O
coap-cli get channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth=1e1017e6-dee7-45b4-8a13-00e6afeb66eb -o
```
```bash
coap-cli post coap://localhost/channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic\?authorization=1e1017e6-dee7-45b4-8a13-00e6afeb66eb -d "hello world"
coap-cli post channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth=1e1017e6-dee7-45b4-8a13-00e6afeb66eb -d "hello world"
```
```bash
coap-cli post channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth=1e1017e6-dee7-45b4-8a13-00e6afeb66eb -d "hello world" -h 0.0.0.0 -p 1234
```
147 changes: 49 additions & 98 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package main

import (
"context"
"errors"
"flag"
"fmt"
"log"
"net/url"
"os"
"os/signal"
"strings"
"syscall"

coap "github.com/mainflux/coap-cli/coap"

gocoap "github.com/dustin/go-coap"
"github.com/plgd-dev/go-coap/v2/message"
coapmsg "github.com/plgd-dev/go-coap/v2/message"
"github.com/plgd-dev/go-coap/v2/message/codes"
"github.com/plgd-dev/go-coap/v2/udp/message/pool"
)

const (
Expand All @@ -20,48 +25,23 @@ const (
delete = "DELETE"
)

func parseCode(code string) (gocoap.COAPCode, error) {
func parseCode(code string) (codes.Code, error) {
switch code {
case get:
return gocoap.GET, nil
return codes.GET, nil
case put:
return gocoap.PUT, nil
return codes.PUT, nil
case post:
return gocoap.POST, nil
return codes.POST, nil
case delete:
return gocoap.DELETE, nil
return codes.DELETE, nil
}
return 0, errors.New("Message can be GET, POST, PUT or DELETE")
}

func checkType(c, n, a, r *bool) (gocoap.COAPType, error) {
arr := []bool{*c, *n, *a, *r}
var counter int
for _, v := range arr {
if v {
counter++
}
}
if counter > 1 {
return 0, errors.New("invalid message type")
}
switch {
case *c:
return gocoap.Confirmable, nil
case *n:
return gocoap.NonConfirmable, nil
case *a:
return gocoap.Acknowledgement, nil
case *r:
return gocoap.Reset, nil
}
return gocoap.Confirmable, nil
}

func printMsg(m *gocoap.Message) {
func printMsg(m *pool.Message) {
if m != nil {
log.Printf("\nMESSAGE:\nType: %d\nCode: %s\nMessageID: %d\nToken: %s\nPayload: %s\n",
m.Type, m.Code.String(), m.MessageID, m.Token, m.Payload)
log.Printf("\nMESSAGE:\n %v", m)
}
}

Expand All @@ -71,85 +51,56 @@ func main() {
}
code, err := parseCode(strings.ToUpper(os.Args[1]))
if err != nil {
log.Fatal("ERROR: ", err)
log.Fatal("error: ", err)
}
if len(os.Args) < 3 {
log.Fatal("Please enter valid CoAP URL")
}
addr := os.Args[2]
path := os.Args[2]
os.Args = os.Args[2:]

c := flag.Bool("C", false, "Confirmable")
n := flag.Bool("NC", false, "Non-confirmable")
a := flag.Bool("ACK", false, "Acknowledgement")
r := flag.Bool("RST", false, "Reset")
o := flag.Bool("O", false, "Observe")
o := flag.Bool("o", false, "Observe")
h := flag.String("h", "localhost", "Host")
p := flag.String("p", "5683", "Port")
// Default type is JSON.
cf := flag.Int("CF", 50, "Content format")
cf := flag.Int("q", 50, "Content format")
d := flag.String("d", "", "Message data")
id := flag.Uint("id", 0, "Message ID")
token := flag.String("token", "", "Message data")
a := flag.String("auth", "", "Auth token")

flag.Parse()

t, err := checkType(c, n, a, r)
fmt.Println(*h + ":" + *p)
client, err := coap.New(*h + ":" + *p)
if err != nil {
log.Fatal("ERR TYPE: ", err)
log.Fatal("Error creating client: ", err)
}
address, err := url.Parse(addr)
if err != nil {
log.Fatal("ERR PARSING ADDR:", err)
}
client, err := coap.New(address)
if err != nil {
log.Fatal("ERROR CREATING CLIENT: ", err)
var opts coapmsg.Options
if a != nil {
opts = append(opts, coapmsg.Option{ID: coapmsg.URIQuery, Value: []byte(fmt.Sprintf("auth=%s", *a))})
}

opts := coap.ParseOptions(address)
if *o {
opts = append(opts, coap.Option{
ID: gocoap.Observe,
Value: 0,
})
}
if *cf != 0 {
opts = append(opts, coap.Option{
ID: gocoap.ContentFormat,
Value: *cf,
})
}
if o == nil {
pld := strings.NewReader(*d)

res, err := client.Send(t, code, uint16(*id), []byte(*token), []byte(*d), opts)
if err != nil {
log.Fatal("ERROR: ", err)
}
printMsg(res)
if res == nil {
os.Exit(0)
}
switch res.Code {
case gocoap.Forbidden, gocoap.BadRequest, gocoap.InternalServerError, gocoap.NotFound:
log.Fatalf("Response code: %s", res.Code)
}
if *o {
if code != gocoap.GET {
log.Fatalln("Can observe non GET requests.")
}
msgs := make(chan *gocoap.Message)
go func() {
for {
msg, err := client.Receive()
if err != nil {
log.Fatal("ERROR RECEIVING: ", err)
}
msgs <- msg
}
}()
for {
select {
case m := <-msgs:
printMsg(m)
}
res, err := client.Send(path, code, message.MediaType(*cf), pld, opts...)
if err != nil {
log.Fatal("Error sending message: ", err)
}
printMsg(res)
return
}
obs, err := client.Receive(path, opts...)
if err != nil {
log.Fatal("Error observing resource: ", err)
}
errs := make(chan error, 2)
go func() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT)
errs <- fmt.Errorf("%s", <-c)
}()

err = <-errs
obs.Cancel(context.Background())
log.Fatal("Observation terminated: ", err)
}
67 changes: 44 additions & 23 deletions coap/client.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,67 @@
package coap

import (
"net/url"
"context"
"errors"
"fmt"
"io"
"log"
"time"

gocoap "github.com/dustin/go-coap"
"github.com/plgd-dev/go-coap/v2/message"
"github.com/plgd-dev/go-coap/v2/message/codes"
"github.com/plgd-dev/go-coap/v2/udp"
"github.com/plgd-dev/go-coap/v2/udp/client"
"github.com/plgd-dev/go-coap/v2/udp/message/pool"
)

// Client represents CoAP client.
type Client struct {
conn *conn
conn *client.ClientConn
}

// New returns new CoAP client connecting it to the server.
func New(addr *url.URL) (Client, error) {
address, err := parseAddr(addr)
func New(addr string) (Client, error) {
c, err := udp.Dial(addr)
if err != nil {
return Client{}, err
}
c, err := dial(address)
if err != nil {
return Client{}, err
log.Fatalf("Error dialing: %v", err)
}

return Client{conn: c}, nil
}

// Send send a message.
func (c Client) Send(msgType gocoap.COAPType, msgCode gocoap.COAPCode, msgID uint16, token, payload []byte, opts []Option) (*gocoap.Message, error) {
msg := gocoap.Message{
Type: msgType,
Code: msgCode,
MessageID: msgID,
Token: token,
Payload: payload,
}
func (c Client) Send(path string, msgCode codes.Code, cf message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

for _, o := range opts {
msg.SetOption(o.ID, o.Value)
switch msgCode {
case codes.GET:
return c.conn.Get(ctx, path, opts...)
case codes.POST:
return c.conn.Post(ctx, path, cf, payload, opts...)
case codes.PUT:
return c.conn.Put(ctx, path, cf, payload, opts...)
case codes.DELETE:
return c.conn.Delete(ctx, path, opts...)
}
return c.conn.send(msg)
return nil, errors.New("Invalid message code")
}

// Receive receives a message.
func (c Client) Receive() (*gocoap.Message, error) {
return c.conn.receive()
func (c Client) Receive(path string, opts ...message.Option) (*client.Observation, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

return c.conn.Observe(ctx, path, func(res *pool.Message) {
fmt.Printf("\nRECEIVED OBSERVE: %v\n", res)
body, err := res.ReadBody()
if err != nil {
fmt.Println("Error reading message body: ", err)
return
}
if len(body) > 0 {
fmt.Println("Payload: ", string(body))
}
}, opts...)
}
35 changes: 0 additions & 35 deletions coap/options.go

This file was deleted.

Loading

0 comments on commit 2e65f08

Please sign in to comment.