lol, lmao even

This commit is contained in:
q3k 2024-08-20 23:39:58 +02:00
commit 9a61264570
5 changed files with 269 additions and 0 deletions

10
go.mod Normal file
View file

@ -0,0 +1,10 @@
module code.hackerspace.pl/q3k/jankssi
go 1.22.3
require go.bug.st/serial v1.6.2
require (
github.com/creack/goselect v0.1.2 // indirect
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
)

14
go.sum Normal file
View file

@ -0,0 +1,14 @@
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

49
main.go Normal file
View file

@ -0,0 +1,49 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"go.bug.st/serial"
)
type FileSerial struct {
f serial.Port
}
func (f *FileSerial) Write(ctx context.Context, data []byte) error {
_, err := f.f.Write(data)
return err
}
func (f *FileSerial) Read(ctx context.Context, data []byte) (int, error) {
n, err := f.f.Read(data)
return n, err
}
func main() {
f, err := serial.Open("/dev/ttyACM0", &serial.Mode{})
if err != nil {
log.Fatalf("Failed to open serial port: %v", err)
}
bC := make(chan []byte)
ssi := NewSSI(&FileSerial{f: f}, func(ty uint8, data []byte) {
log.Printf("Barcode: %x/%s", ty, data)
select {
case bC <- data:
default:
}
})
ctx := context.Background()
go ssi.Run(ctx)
http.HandleFunc("/barcode", func(w http.ResponseWriter, r *http.Request) {
b := <-bC
fmt.Fprintf(w, "barcode: %s", b)
})
http.ListenAndServe(":2137", nil)
}

179
ssi.go Normal file
View file

@ -0,0 +1,179 @@
package main
import (
"bytes"
"context"
"encoding/binary"
"errors"
"fmt"
"log"
"sync"
"time"
)
type CallbackOnBarcode func(ty uint8, data []byte)
type SerialPort interface {
Write(ctx context.Context, data []byte) error
Read(ctx context.Context, out []byte) (int, error)
}
type CommandID uint8
const (
CommandIDBeep CommandID = 0xe6
CommandIDAck CommandID = 0xd0
CommandDecodeData CommandID = 0xf3
)
type Flag uint8
const (
FlagRetransmission Flag = 1
)
type Direction uint8
const (
DirectionFromHost Direction = 4
DirectionFromDevice Direction = 0
)
type Command struct {
ID CommandID
Direction Direction
Flags Flag
Data []byte
}
func (c *Command) Serialize() []byte {
buf := bytes.NewBuffer(nil)
binary.Write(buf, binary.BigEndian, c.ID)
binary.Write(buf, binary.BigEndian, c.Direction)
binary.Write(buf, binary.BigEndian, c.Flags)
buf.Write(c.Data)
res := buf.Bytes()
res = append([]byte{uint8(len(res) + 1)}, res...)
sum := int16(0)
for _, b := range res {
sum += int16(b)
}
sum = -sum
buf = bytes.NewBuffer(res)
binary.Write(buf, binary.BigEndian, sum)
return buf.Bytes()
}
type SSI struct {
serial SerialPort
mu sync.Mutex
pendingOut *Command
cbOnBarcode CallbackOnBarcode
}
func NewSSI(serial SerialPort, cbOnBarcode CallbackOnBarcode) *SSI {
return &SSI{
serial: serial,
cbOnBarcode: cbOnBarcode,
}
}
func (s *SSI) reset() {
time.Sleep(time.Second)
// flush in/out
}
func (s *SSI) Run(ctx context.Context) error {
for {
buf := make([]byte, 1)
_, err := s.serial.Read(ctx, buf)
if err != nil {
return fmt.Errorf("reading length: %w", err)
}
size := buf[0]
if size < 4 {
// Packet too small, desync.
log.Printf("Packet too small")
s.reset()
continue
}
rest := make([]byte, size+1)
ctxT, ctxC := context.WithTimeout(ctx, time.Millisecond*100)
_, err = s.serial.Read(ctxT, rest)
ctxC()
if err != nil {
if errors.Is(err, ctxT.Err()) {
// Timeout ocurred, reset state machine
log.Printf("Read timeout")
s.reset()
continue
}
return fmt.Errorf("reading rest of packet: %w", err)
}
checksumB := rest[len(rest)-2:]
body := rest[:len(rest)-2]
var checksum int16
binary.Read(bytes.NewBuffer(checksumB), binary.BigEndian, &checksum)
sum := int16(size)
for _, b := range body {
sum += int16(b)
}
if sum != -checksum {
// Desynnc
log.Printf("Checksum failed, wanted %d, got %d", sum, -checksum)
s.reset()
}
cmd := Command{
ID: CommandID(body[0]),
Direction: Direction(body[1]),
Flags: Flag(body[2]),
Data: body[3:],
}
if err := s.handleIn(ctx, cmd); err != nil {
return err
}
}
}
func (s *SSI) handleIn(ctx context.Context, cmd Command) error {
s.mu.Lock()
defer s.mu.Unlock()
// TODO: handle received retransmissions
switch cmd.ID {
case CommandIDAck:
if s.pendingOut != nil {
s.pendingOut = nil
} else {
log.Printf("Received ack for unknown packet")
s.reset()
}
return nil
case CommandDecodeData:
if (cmd.Flags & FlagRetransmission) == 0 {
s.cbOnBarcode(cmd.Data[0], cmd.Data[1:])
} else {
log.Printf("Retransmitted data from device")
}
ack := Command{
ID: CommandIDAck,
Direction: DirectionFromHost,
}
return s.serial.Write(ctx, ack.Serialize())
default:
log.Printf("Unknown command received: %+v", cmd)
s.reset()
return nil
}
}
func (s *SSI) Beep(ctx context.Context) error {
return nil
}

17
ssi_test.go Normal file
View file

@ -0,0 +1,17 @@
package main
import (
"bytes"
"testing"
)
func TestSerialize(t *testing.T) {
cmd := Command{
ID: CommandIDBeep,
Direction: DirectionFromHost,
Data: []byte{1},
}
if want, got := []byte{0x05, 0xe6, 0x04, 0x00, 0x01, 0xff, 0x10}, cmd.Serialize(); !bytes.Equal(want, got) {
t.Fatalf("Serialize failed: wanted %+v, got %+v", want, got)
}
}