Skip to content

Instantly share code, notes, and snippets.

@KunYi
Created March 28, 2020 10:26
Show Gist options
  • Save KunYi/7ea6f0de39aeedabcd5177ae79b43282 to your computer and use it in GitHub Desktop.
Save KunYi/7ea6f0de39aeedabcd5177ae79b43282 to your computer and use it in GitHub Desktop.
/* for Sanica SNP-400 packing system testing */
package main
/*
for Sanica SNP-42x packing system testing
using RS485 communcation to control the
*/
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"time"
/*
don't use the following package on windows
because them depandance libserialport
"github.com/facchinm/go-serial-native"
"github.com/mikepb/go-serial"
*/
"github.com/sigurn/crc16"
"github.com/tarm/serial"
)
//------------------------------------------------
// config zone
//
const bDBG = true
const strSerialPort = "COM10"
// const strSerialPort = "/dev/ttyO1"
// const strSerialPort = "/dev/ttyO4"
var comConfig = serial.Config{
Name: strSerialPort,
Baud: 9600,
Parity: serial.ParityNone,
Size: 8,
StopBits: serial.Stop1,
}
const retryCounter = 5
// timeout, unit: millisecond
const (
resTelTime = 1000
resCmdTime = 500
)
const (
startDev = 1
endDev = 2
)
const hextable = "0123456789abcdef"
func encodePacketToString(src []byte) string {
dst := make([]byte, len(src)*4)
for i, v := range src {
dst[i*4] = ' '
dst[i*4+1] = hextable[v>>4]
dst[i*4+2] = hextable[v&0x0f]
dst[i*4+3] = 'H'
}
return string(dst)
}
//------------------------------------------------
var crcTable = crc16.MakeTable(crc16.CRC16_ARC)
func poll(addr int) (packet []byte) {
t := []byte(fmt.Sprintf("%02d", addr))
return []byte{0x23, t[0], t[1], ENQ}
}
func endTrans(addr int) (packet []byte) {
t := []byte(fmt.Sprintf("%02d", addr))
return []byte{TEL, t[0], t[1], EOT}
}
func queryStatus(addr int) (packet []byte) {
t := []byte(fmt.Sprintf("%02d", addr))
p := []byte{SOH, 0, 0, STX, 'P', 'S', ETX, 0x00, 0x00, 0x00, 0x00}
crc := []byte(fmt.Sprintf("%04X", crc16.Checksum(p[4:7], crcTable)))
copy(p[1:3], t[0:2]) // for address
copy(p[7:], crc) // for CRC16
return p
}
func sendCtrl(addr int, ctlLoop int, ctlLocking int) (packet []byte) {
var p strings.Builder
var t strings.Builder
p.Grow(20)
t.Grow(10)
p.WriteByte(SOH)
p.WriteString(fmt.Sprintf("%02d", addr))
p.WriteByte(STX)
t.WriteString("PC") // command
switch ctlLoop {
case ctrON:
t.WriteByte('1')
case ctrOFF:
t.WriteByte('2')
default:
t.WriteByte('0')
}
switch ctlLocking {
case ctlDOWN:
t.WriteByte('1')
case ctrUP:
t.WriteByte('2')
default:
t.WriteByte('0')
}
t.WriteByte(ETX)
p.WriteString(t.String())
p.WriteString(fmt.Sprintf("%04X",
crc16.Checksum([]byte(t.String()), crcTable)))
return []byte(p.String())
}
// LockingStatus fpr store locking
type LockingStatus struct {
addr int
loop int
mat int
locking int
sensor int // plate location
loopCnt int
OffBaseCnt int
OnLvCnt int
OffLvCnt int
OnBaseCnt int
}
type respLockingStatus struct {
rType int
rStatus LockingStatus
}
const (
// LpNA for non available, non change
LpNA = 0
// LpOff for Off of Loop
LpOff = 1
// LpOn for Off of Lopp
LpOn = 2
// LpErr for Off of Loop
LpErr = 3
// LpForceOff for forced pff of loop
LpForceOff = 3
// LpForceOn for forced on of looop
LpForceOn = 4
)
const (
// matNA for non available, non change
matNA = 0
// matOff for
matOff = 1
// matOn for
matOn = 2
)
const (
// LkStandy for non available, non change
LkStandy = 0
// LkDec for Locking plate Declining
LkDec = 1
// LkInc for Locking plate Inclining
LkInc = 2
// LkErrDec for
LkErrDec = 3
// LkErrInc for
LkErrInc = 4
// LkForceDec for
LkForceDec = 5
// LkForceInc for
LkForceInc = 6
// LkIrregal for
LkIrregal = 0x0D
)
const (
// LsMiddle for Middle
LsMiddle = 0
// LsBottom for Bottom End
LsBottom = 1
// LsTop for Top
LsTop = 2
// LsError for Error
LsError = 3
)
// Packet for processing packet
type Packet struct {
packetType int
addr string
cmd string
data string
crc string
}
func (p Packet) crc16() string {
var t strings.Builder
t.WriteString(p.cmd)
t.WriteString(p.data)
t.WriteByte(ETX)
return fmt.Sprintf("%04X", crc16.Checksum([]byte(t.String()), crcTable))
}
// RxState for process packet using
type RxState struct {
state int
procTime time.Time
packet Packet
}
var rxState = RxState{state: rxIdle}
var rxPacket = make(chan Packet, 20)
type procRecv func(io.Reader)
// package const charactor
const (
TEL byte = '#'
SOH byte = 0x01
STX byte = 0x02
ETX byte = 0x03
EOT byte = 0x04
ENQ byte = 0x05
ACK byte = 0x06
DLE byte = 0x10
NAK byte = 0x15
DEL byte = 0x7F
EOD byte = 0x80
ctrNON int = '0'
ctlDOWN int = '1'
ctrUP int = '2'
ctrOFF int = '1'
ctrON int = '2'
)
const (
rxIdle = 0
rxTelStart = 1
rxTelAdr1 = 2
rxTelAdr2 = 3
rxTelEnd = 4
rxCmdAdr1 = 5
rxCmdAdr2 = 6
rxCmdTxt = 7
rxCmdTyp1 = 8
rxCmdTyp2 = 9
rxCmdETx = 10
rxCmdCRC = 11
)
const (
packetTEL = 0
packetCMD = 1
)
func recv(port *serial.Port, procPacket procRecv) {
for {
buf := make([]byte, 256)
if n, err := port.Read(buf); err != nil {
log.Panic(err)
} else {
//println("got rx, n:" + strconv.Itoa(n))
procPacket(bytes.NewReader(buf[:n]))
}
}
}
func procRx(data io.Reader) {
b := make([]byte, 1)
// for packet timeout
if rxState.state != rxIdle {
if time.Now().Sub(rxState.procTime) > 30*time.Millisecond {
rxState.state = rxIdle
}
}
for {
if _, err := data.Read(b); err != nil {
if err != io.EOF {
log.Panic(err)
}
break
}
// assume t to data
t := b[0]
print(encodePacketToString(b) + " ")
rxState.procTime = time.Now()
switch state := rxState.state; state {
case rxIdle:
if t == TEL {
rxState.state = rxTelAdr1
rxState.packet.packetType = packetTEL
//println("proc rxTel")
} else if t == SOH {
rxState.state = rxCmdAdr1
rxState.packet.packetType = packetCMD
//println("proc rxCmd")
}
case rxTelAdr1, rxTelAdr2:
if t < '0' || t > '9' {
rxState.state = rxIdle
println("failed reback rxIdle")
} else {
if state == rxTelAdr1 {
rxState.packet.addr = string(t)
rxState.state = rxTelAdr2
} else {
rxState.packet.addr += string(t)
rxState.state = rxTelEnd
}
}
case rxTelEnd:
switch t {
case ENQ:
rxState.packet.data = "ENQ"
rxPacket <- rxState.packet
case ACK:
rxState.packet.data = "ACK"
rxPacket <- rxState.packet
case NAK:
log.Println("Got NAK of Poll packet")
rxState.packet.data = "NAK"
rxPacket <- rxState.packet
case EOT:
rxState.packet.data = "EOT"
rxPacket <- rxState.packet
default:
log.Println("Got err Poll packet")
}
rxState.state = rxIdle
case rxCmdAdr1, rxCmdAdr2:
if t < '0' || t > '9' {
rxState.state = rxIdle
} else {
if state == rxCmdAdr1 {
rxState.packet.addr = string(t)
rxState.state = rxCmdAdr2
} else {
rxState.packet.addr += string(t)
rxState.state = rxCmdTxt
}
}
case rxCmdTxt:
if t != STX {
rxState.state = rxIdle
}
rxState.state = rxCmdTyp1
case rxCmdTyp1, rxCmdTyp2:
if t < 'A' || t > 'Z' {
rxState.state = rxIdle
}
if state == rxCmdTyp1 {
rxState.packet.cmd = string(t)
rxState.state = rxCmdTyp2
} else {
rxState.packet.cmd += string(t)
rxState.packet.data = "" // clean data
rxState.state = rxCmdETx
}
case rxCmdETx:
if t != ETX {
rxState.packet.data += string(t)
} else {
rxState.state = rxCmdCRC
rxState.packet.crc = ""
}
case rxCmdCRC:
if len(rxState.packet.crc) < 4 {
rxState.packet.crc += string(t)
}
if len(rxState.packet.crc) == 4 {
rxState.state = rxIdle
rxPacket <- rxState.packet
}
}
}
}
func procTelPacket(packet <-chan Packet, commAddr int) (success bool, err error) {
to := time.NewTimer((10 + resTelTime) * time.Millisecond)
sAddr := fmt.Sprintf("%02d", commAddr)
for {
// (11bits*4bytes)*2(tx/rx) ~= 10ms
// we add 200ms for remote process and response
to.Reset((10 + resTelTime) * time.Millisecond)
select {
case data := <-packet:
if packetTEL == data.packetType {
if sAddr == data.addr {
switch r := data.data; r {
case "ACK":
return true, nil
case "NAK":
return false, errors.New("NAK")
}
}
}
case <-to.C:
return false, errors.New("timeout")
}
}
}
func sendHostIdentify(port *serial.Port, addr int, packet []byte) (respLockingStatus, error) {
if bDBG {
log.Println("send => plate:" + encodePacketToString(packet))
}
for i := 0; i < retryCounter; i++ {
port.Write(packet)
if r, e := procPSPacket(rxPacket, addr); e != nil {
if e.Error() == "NAK" {
log.Println("NAK")
} else if e.Error() == "timeout" {
println("")
log.Println("timeout")
} else {
log.Println(e)
}
} else {
return r, nil
}
}
return respLockingStatus{rType: -1}, errors.New("Failed no response")
}
func sendCtrlPacket(port *serial.Port, addr int, packet []byte) bool {
if bDBG {
log.Println("send => plate:" + encodePacketToString(packet))
}
for i := 0; i < 3; i++ {
port.Write(packet)
if _, e := procTelPacket(rxPacket, addr); e != nil {
if e.Error() == "timeout" {
log.Println("timeout")
} else {
log.Println(e)
}
} else {
return true
}
}
return false
}
func convDataStringToStatus(addr int, d string, r *respLockingStatus) {
r.rType = len(d)
r.rStatus.addr = addr
if r.rType >= 4 {
r.rStatus.loop = int(d[0] - byte('0'))
r.rStatus.mat = int(d[1] - byte('0'))
r.rStatus.locking = int(d[2] - byte('0'))
r.rStatus.sensor = int(d[3] - byte('0'))
}
if r.rType >= 8 {
if d, e := strconv.ParseInt(d[4:8], 16, 32); e == nil {
r.rStatus.loopCnt = int(d)
}
}
if r.rType >= 12 {
if d, e := strconv.ParseInt(d[8:12], 16, 32); e == nil {
r.rStatus.OffBaseCnt = int(d)
}
}
if r.rType >= 16 {
if d, e := strconv.ParseInt(d[12:16], 16, 32); e == nil {
r.rStatus.OnLvCnt = int(d)
}
}
if r.rType >= 20 {
if d, e := strconv.ParseInt(d[16:20], 16, 32); e == nil {
r.rStatus.OffLvCnt = int(d)
}
}
if r.rType >= 24 {
if d, e := strconv.ParseInt(d[20:24], 16, 32); e == nil {
r.rStatus.OnBaseCnt = int(d)
}
}
}
func procPSPacket(packet <-chan Packet, commAddr int) (status respLockingStatus, err error) {
to := time.NewTimer((22 + resCmdTime) * time.Millisecond)
sAddr := fmt.Sprintf("%02d", commAddr)
for {
// (11bits*13bytes)*2(tx/rx) ~= 22ms
// we add 500ms for remote process and response
to.Reset((22 + resCmdTime) * time.Millisecond)
select {
case data := <-packet:
if packetCMD == data.packetType {
if sAddr == data.addr {
s := respLockingStatus{rType: -1}
if data.crc16() == data.crc {
println("")
log.Println(fmt.Sprintf("got Device:%s, Data:%s", data.addr, data.data))
addr, _ := strconv.Atoi(data.addr)
convDataStringToStatus(addr, data.data, &s)
} else {
log.Println(fmt.Sprintf("Failed: CRC check, Device:%s, Data:%s", data.addr, data.data))
return s, errors.New("crc")
}
return s, nil
}
}
case <-to.C:
return respLockingStatus{rType: 0}, errors.New("timeout")
}
}
}
func procCmdPacket(packet <-chan Packet, commAddr int) (status respLockingStatus, err error) {
to := time.NewTimer((22 + resCmdTime) * time.Millisecond)
sAddr := fmt.Sprintf("%02d", commAddr)
for {
// (11bits*13bytes)*2(tx/rx) ~= 22ms
// we add 500ms for remote process and response
to.Reset((22 + resCmdTime) * time.Millisecond)
select {
case data := <-packet:
if packetCMD == data.packetType {
if sAddr == data.addr {
s := respLockingStatus{rType: -1}
if data.crc16() == data.crc {
println("")
log.Println(fmt.Sprintf("got Device:%s, Data:%s", data.addr, data.data))
addr, _ := strconv.Atoi(data.addr)
convDataStringToStatus(addr, data.data, &s)
} else {
log.Println(fmt.Sprintf("Failed: CRC check, Device:%s, Data:%s", data.addr, data.data))
return s, errors.New("crc")
}
return s, nil
}
}
case <-to.C:
return respLockingStatus{rType: 0}, errors.New("timeout")
}
}
}
func sendCmdPacket(port *serial.Port, addr int, packet []byte) (respLockingStatus, error) {
for i := 0; i < 3; i++ {
port.Write(packet)
if _, e := procCmdPacket(rxPacket, addr); e != nil {
if e.Error() == "timeout" {
log.Println("timeout")
} else {
log.Println(e)
}
} else {
return respLockingStatus{rType: 1}, nil
}
}
return respLockingStatus{rType: -1}, errors.New("Failed no response")
}
func updateLockingStatus(t *LockingStatus, r *respLockingStatus) {
if r.rType >= 4 {
t.loop = r.rStatus.loop
t.mat = r.rStatus.mat
t.locking = r.rStatus.locking
t.sensor = r.rStatus.sensor
}
if r.rType >= 8 {
t.loopCnt = r.rStatus.loopCnt
}
if r.rType >= 12 {
t.OffBaseCnt = r.rStatus.OffBaseCnt
}
if r.rType >= 16 {
t.OnLvCnt = r.rStatus.OnLvCnt
}
if r.rType >= 20 {
t.OffLvCnt = r.rStatus.OffLvCnt
}
if r.rType >= 24 {
t.OnBaseCnt = r.rStatus.OnBaseCnt
}
}
func main() {
reader := bufio.NewReader(os.Stdin)
c0 := &comConfig
p, _ := serial.OpenPort(c0)
defer p.Close()
go recv(p, procRx)
snpMap := map[int]LockingStatus{}
for commAdr := startDev; commAdr < endDev; commAdr++ {
if r, err := sendHostIdentify(p, commAdr, poll(commAdr)); err == nil {
// find device
log.Println(fmt.Sprintf("find device at %02d", commAdr))
t := LockingStatus{addr: commAdr}
updateLockingStatus(&t, &r)
snpMap[commAdr] = t
}
}
go func() {
for {
log.Println("")
log.Print("CMD>>")
if cmdString, err := reader.ReadString('\n'); err == nil {
cmdString = strings.TrimSuffix(cmdString, "\n")
cmds := strings.Split(cmdString, " ")
snp := 1
switch cmd := strings.ToLower(cmds[0]); cmd {
case "up":
log.Println("Up plate device:", cmds[1])
sendCtrlPacket(p, snp, sendCtrl(snp, ctrNON, ctrUP))
case "down":
log.Println("Down plate device:", cmds[1])
sendCtrlPacket(p, snp, sendCtrl(snp, ctrNON, ctlDOWN))
case "on":
log.Println("On Loop device:", cmds[1])
case "off":
log.Println("Off Loop device:", cmds[1])
default:
log.Println("Unknow command")
}
}
}
}()
for {
time.Sleep(4 * time.Second)
/*
for snp := range snpMap {
time.Sleep(20 * time.Millisecond)
//if sendCtrlPacket(p, snp, sendCtrl(snp, ctrNON, ctrUP))
//log.Println("send command" + strconv.Itoa(snp))
}
*/
}
}
/*
ssh -R 52698:127.0.0.1:52698 [email protected]
ssh -R 52698:127.0.0.1:52698 [email protected]
ssh -R 52698:127.0.0.1:52698 [email protected]
on remote linux SRT3352
gomate
*/
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"time"
/*
don't use the following package on windows
because them depandance libserialport
"github.com/facchinm/go-serial-native"
"github.com/mikepb/go-serial"
*/
"github.com/sigurn/crc16"
"github.com/tarm/serial"
)
//------------------------------------------------
// config zone
//
const bDBG = true
const strSerialPort = "COM10"
// const strSerialPort = "/dev/ttyO1"
// const strSerialPort = "/dev/ttyO4"
var comConfig = serial.Config{
Name: strSerialPort,
Baud: 9600,
Parity: serial.ParityNone,
Size: 8,
StopBits: serial.Stop1,
}
const retryCounter = 5
// timeout, unit: millisecond
const (
resTelTime = 1000
resCmdTime = 500
)
const (
startDev = 1
endDev = 2
)
const hextable = "0123456789abcdef"
func encodePacketToString(src []byte) string {
dst := make([]byte, len(src)*4)
for i, v := range src {
dst[i*4] = ' '
dst[i*4+1] = hextable[v>>4]
dst[i*4+2] = hextable[v&0x0f]
dst[i*4+3] = 'H'
}
return string(dst)
}
//------------------------------------------------
var crcTable = crc16.MakeTable(crc16.CRC16_ARC)
func poll(addr int) (packet []byte) {
t := []byte(fmt.Sprintf("%02d", addr))
return []byte{0x23, t[0], t[1], ENQ}
}
func endTrans(addr int) (packet []byte) {
t := []byte(fmt.Sprintf("%02d", addr))
return []byte{TEL, t[0], t[1], EOT}
}
func queryStatus(addr int) (packet []byte) {
t := []byte(fmt.Sprintf("%02d", addr))
p := []byte{SOH, 0, 0, STX, 'P', 'S', ETX, 0x00, 0x00, 0x00, 0x00}
crc := []byte(fmt.Sprintf("%04X", crc16.Checksum(p[4:7], crcTable)))
copy(p[1:3], t[0:2]) // for address
copy(p[7:], crc) // for CRC16
return p
}
func sendCtrl(addr int, ctlLoop int, ctlLocking int) (packet []byte) {
var p strings.Builder
var t strings.Builder
p.Grow(20)
t.Grow(10)
p.WriteByte(SOH)
p.WriteString(fmt.Sprintf("%02d", addr))
p.WriteByte(STX)
t.WriteString("PC") // command
switch ctlLoop {
case ctrON:
t.WriteByte('1')
case ctrOFF:
t.WriteByte('2')
default:
t.WriteByte('0')
}
switch ctlLocking {
case ctlDOWN:
t.WriteByte('1')
case ctrUP:
t.WriteByte('2')
default:
t.WriteByte('0')
}
t.WriteByte(ETX)
p.WriteString(t.String())
p.WriteString(fmt.Sprintf("%04X",
crc16.Checksum([]byte(t.String()), crcTable)))
return []byte(p.String())
}
// LockingStatus fpr store locking
type LockingStatus struct {
addr int
loop int
mat int
locking int
sensor int // plate location
loopCnt int
OffBaseCnt int
OnLvCnt int
OffLvCnt int
OnBaseCnt int
}
type respLockingStatus struct {
rType int
rStatus LockingStatus
}
const (
// LpNA for non available, non change
LpNA = 0
// LpOff for Off of Loop
LpOff = 1
// LpOn for Off of Lopp
LpOn = 2
// LpErr for Off of Loop
LpErr = 3
// LpForceOff for forced pff of loop
LpForceOff = 3
// LpForceOn for forced on of looop
LpForceOn = 4
)
const (
// matNA for non available, non change
matNA = 0
// matOff for
matOff = 1
// matOn for
matOn = 2
)
const (
// LkStandy for non available, non change
LkStandy = 0
// LkDec for Locking plate Declining
LkDec = 1
// LkInc for Locking plate Inclining
LkInc = 2
// LkErrDec for
LkErrDec = 3
// LkErrInc for
LkErrInc = 4
// LkForceDec for
LkForceDec = 5
// LkForceInc for
LkForceInc = 6
// LkIrregal for
LkIrregal = 0x0D
)
const (
// LsMiddle for Middle
LsMiddle = 0
// LsBottom for Bottom End
LsBottom = 1
// LsTop for Top
LsTop = 2
// LsError for Error
LsError = 3
)
// Packet for processing packet
type Packet struct {
packetType int
addr string
cmd string
data string
crc string
}
func (p Packet) crc16() string {
var t strings.Builder
t.WriteString(p.cmd)
t.WriteString(p.data)
t.WriteByte(ETX)
return fmt.Sprintf("%04X", crc16.Checksum([]byte(t.String()), crcTable))
}
// RxState for process packet using
type RxState struct {
state int
procTime time.Time
packet Packet
}
var rxState = RxState{state: rxIdle}
var rxPacket = make(chan Packet, 20)
type procRecv func(io.Reader)
// package const charactor
const (
TEL byte = '#'
SOH byte = 0x01
STX byte = 0x02
ETX byte = 0x03
EOT byte = 0x04
ENQ byte = 0x05
ACK byte = 0x06
DLE byte = 0x10
NAK byte = 0x15
DEL byte = 0x7F
EOD byte = 0x80
ctrNON int = '0'
ctlDOWN int = '1'
ctrUP int = '2'
ctrOFF int = '1'
ctrON int = '2'
)
const (
rxIdle = 0
rxTelStart = 1
rxTelAdr1 = 2
rxTelAdr2 = 3
rxTelEnd = 4
rxCmdAdr1 = 5
rxCmdAdr2 = 6
rxCmdTxt = 7
rxCmdTyp1 = 8
rxCmdTyp2 = 9
rxCmdETx = 10
rxCmdCRC = 11
)
const (
packetTEL = 0
packetCMD = 1
)
func recv(port *serial.Port, procPacket procRecv) {
for {
buf := make([]byte, 256)
if n, err := port.Read(buf); err != nil {
log.Panic(err)
} else {
//println("got rx, n:" + strconv.Itoa(n))
procPacket(bytes.NewReader(buf[:n]))
}
}
}
func procRx(data io.Reader) {
b := make([]byte, 1)
// for packet timeout
if rxState.state != rxIdle {
if time.Now().Sub(rxState.procTime) > 30*time.Millisecond {
rxState.state = rxIdle
}
}
for {
if _, err := data.Read(b); err != nil {
if err != io.EOF {
log.Panic(err)
}
break
}
// assume t to data
t := b[0]
print(encodePacketToString(b) + " ")
rxState.procTime = time.Now()
switch state := rxState.state; state {
case rxIdle:
if t == TEL {
rxState.state = rxTelAdr1
rxState.packet.packetType = packetTEL
//println("proc rxTel")
} else if t == SOH {
rxState.state = rxCmdAdr1
rxState.packet.packetType = packetCMD
//println("proc rxCmd")
}
case rxTelAdr1, rxTelAdr2:
if t < '0' || t > '9' {
rxState.state = rxIdle
println("failed reback rxIdle")
} else {
if state == rxTelAdr1 {
rxState.packet.addr = string(t)
rxState.state = rxTelAdr2
} else {
rxState.packet.addr += string(t)
rxState.state = rxTelEnd
}
}
case rxTelEnd:
switch t {
case ENQ:
rxState.packet.data = "ENQ"
rxPacket <- rxState.packet
case ACK:
rxState.packet.data = "ACK"
rxPacket <- rxState.packet
case NAK:
log.Println("Got NAK of Poll packet")
rxState.packet.data = "NAK"
rxPacket <- rxState.packet
case EOT:
rxState.packet.data = "EOT"
rxPacket <- rxState.packet
default:
log.Println("Got err Poll packet")
}
rxState.state = rxIdle
case rxCmdAdr1, rxCmdAdr2:
if t < '0' || t > '9' {
rxState.state = rxIdle
} else {
if state == rxCmdAdr1 {
rxState.packet.addr = string(t)
rxState.state = rxCmdAdr2
} else {
rxState.packet.addr += string(t)
rxState.state = rxCmdTxt
}
}
case rxCmdTxt:
if t != STX {
rxState.state = rxIdle
}
rxState.state = rxCmdTyp1
case rxCmdTyp1, rxCmdTyp2:
if t < 'A' || t > 'Z' {
rxState.state = rxIdle
}
if state == rxCmdTyp1 {
rxState.packet.cmd = string(t)
rxState.state = rxCmdTyp2
} else {
rxState.packet.cmd += string(t)
rxState.packet.data = "" // clean data
rxState.state = rxCmdETx
}
case rxCmdETx:
if t != ETX {
rxState.packet.data += string(t)
} else {
rxState.state = rxCmdCRC
rxState.packet.crc = ""
}
case rxCmdCRC:
if len(rxState.packet.crc) < 4 {
rxState.packet.crc += string(t)
}
if len(rxState.packet.crc) == 4 {
rxState.state = rxIdle
rxPacket <- rxState.packet
}
}
}
}
func procTelPacket(packet <-chan Packet, commAddr int) (success bool, err error) {
to := time.NewTimer((10 + resTelTime) * time.Millisecond)
sAddr := fmt.Sprintf("%02d", commAddr)
for {
// (11bits*4bytes)*2(tx/rx) ~= 10ms
// we add 200ms for remote process and response
to.Reset((10 + resTelTime) * time.Millisecond)
select {
case data := <-packet:
if packetTEL == data.packetType {
if sAddr == data.addr {
switch r := data.data; r {
case "ACK":
return true, nil
case "NAK":
return false, errors.New("NAK")
}
}
}
case <-to.C:
return false, errors.New("timeout")
}
}
}
func sendHostIdentify(port *serial.Port, addr int, packet []byte) (respLockingStatus, error) {
if bDBG {
log.Println("send => plate:" + encodePacketToString(packet))
}
for i := 0; i < retryCounter; i++ {
port.Write(packet)
if r, e := procPSPacket(rxPacket, addr); e != nil {
if e.Error() == "NAK" {
log.Println("NAK")
} else if e.Error() == "timeout" {
println("")
log.Println("timeout")
} else {
log.Println(e)
}
} else {
return r, nil
}
}
return respLockingStatus{rType: -1}, errors.New("Failed no response")
}
func sendCtrlPacket(port *serial.Port, addr int, packet []byte) bool {
if bDBG {
log.Println("send => plate:" + encodePacketToString(packet))
}
for i := 0; i < 3; i++ {
port.Write(packet)
if _, e := procTelPacket(rxPacket, addr); e != nil {
if e.Error() == "timeout" {
log.Println("timeout")
} else {
log.Println(e)
}
} else {
return true
}
}
return false
}
func convDataStringToStatus(addr int, d string, r *respLockingStatus) {
r.rType = len(d)
r.rStatus.addr = addr
if r.rType >= 4 {
r.rStatus.loop = int(d[0] - byte('0'))
r.rStatus.mat = int(d[1] - byte('0'))
r.rStatus.locking = int(d[2] - byte('0'))
r.rStatus.sensor = int(d[3] - byte('0'))
}
if r.rType >= 8 {
if d, e := strconv.ParseInt(d[4:8], 16, 32); e == nil {
r.rStatus.loopCnt = int(d)
}
}
if r.rType >= 12 {
if d, e := strconv.ParseInt(d[8:12], 16, 32); e == nil {
r.rStatus.OffBaseCnt = int(d)
}
}
if r.rType >= 16 {
if d, e := strconv.ParseInt(d[12:16], 16, 32); e == nil {
r.rStatus.OnLvCnt = int(d)
}
}
if r.rType >= 20 {
if d, e := strconv.ParseInt(d[16:20], 16, 32); e == nil {
r.rStatus.OffLvCnt = int(d)
}
}
if r.rType >= 24 {
if d, e := strconv.ParseInt(d[20:24], 16, 32); e == nil {
r.rStatus.OnBaseCnt = int(d)
}
}
}
func procPSPacket(packet <-chan Packet, commAddr int) (status respLockingStatus, err error) {
to := time.NewTimer((22 + resCmdTime) * time.Millisecond)
sAddr := fmt.Sprintf("%02d", commAddr)
for {
// (11bits*13bytes)*2(tx/rx) ~= 22ms
// we add 500ms for remote process and response
to.Reset((22 + resCmdTime) * time.Millisecond)
select {
case data := <-packet:
if packetCMD == data.packetType {
if sAddr == data.addr {
s := respLockingStatus{rType: -1}
if data.crc16() == data.crc {
println("")
log.Println(fmt.Sprintf("got Device:%s, Data:%s", data.addr, data.data))
addr, _ := strconv.Atoi(data.addr)
convDataStringToStatus(addr, data.data, &s)
} else {
log.Println(fmt.Sprintf("Failed: CRC check, Device:%s, Data:%s", data.addr, data.data))
return s, errors.New("crc")
}
return s, nil
}
}
case <-to.C:
return respLockingStatus{rType: 0}, errors.New("timeout")
}
}
}
func procCmdPacket(packet <-chan Packet, commAddr int) (status respLockingStatus, err error) {
to := time.NewTimer((22 + resCmdTime) * time.Millisecond)
sAddr := fmt.Sprintf("%02d", commAddr)
for {
// (11bits*13bytes)*2(tx/rx) ~= 22ms
// we add 500ms for remote process and response
to.Reset((22 + resCmdTime) * time.Millisecond)
select {
case data := <-packet:
if packetCMD == data.packetType {
if sAddr == data.addr {
s := respLockingStatus{rType: -1}
if data.crc16() == data.crc {
println("")
log.Println(fmt.Sprintf("got Device:%s, Data:%s", data.addr, data.data))
addr, _ := strconv.Atoi(data.addr)
convDataStringToStatus(addr, data.data, &s)
} else {
log.Println(fmt.Sprintf("Failed: CRC check, Device:%s, Data:%s", data.addr, data.data))
return s, errors.New("crc")
}
return s, nil
}
}
case <-to.C:
return respLockingStatus{rType: 0}, errors.New("timeout")
}
}
}
func sendCmdPacket(port *serial.Port, addr int, packet []byte) (respLockingStatus, error) {
for i := 0; i < 3; i++ {
port.Write(packet)
if _, e := procCmdPacket(rxPacket, addr); e != nil {
if e.Error() == "timeout" {
log.Println("timeout")
} else {
log.Println(e)
}
} else {
return respLockingStatus{rType: 1}, nil
}
}
return respLockingStatus{rType: -1}, errors.New("Failed no response")
}
func updateLockingStatus(t *LockingStatus, r *respLockingStatus) {
if r.rType >= 4 {
t.loop = r.rStatus.loop
t.mat = r.rStatus.mat
t.locking = r.rStatus.locking
t.sensor = r.rStatus.sensor
}
if r.rType >= 8 {
t.loopCnt = r.rStatus.loopCnt
}
if r.rType >= 12 {
t.OffBaseCnt = r.rStatus.OffBaseCnt
}
if r.rType >= 16 {
t.OnLvCnt = r.rStatus.OnLvCnt
}
if r.rType >= 20 {
t.OffLvCnt = r.rStatus.OffLvCnt
}
if r.rType >= 24 {
t.OnBaseCnt = r.rStatus.OnBaseCnt
}
}
func main() {
reader := bufio.NewReader(os.Stdin)
c0 := &comConfig
p, _ := serial.OpenPort(c0)
defer p.Close()
go recv(p, procRx)
snpMap := map[int]LockingStatus{}
for commAdr := startDev; commAdr < endDev; commAdr++ {
if r, err := sendHostIdentify(p, commAdr, poll(commAdr)); err == nil {
// find device
log.Println(fmt.Sprintf("find device at %02d", commAdr))
t := LockingStatus{addr: commAdr}
updateLockingStatus(&t, &r)
snpMap[commAdr] = t
}
}
go func() {
for {
log.Println("")
log.Print("CMD>>")
if cmdString, err := reader.ReadString('\n'); err == nil {
cmdString = strings.TrimSuffix(cmdString, "\n")
cmds := strings.Split(cmdString, " ")
snp := 1
switch cmd := strings.ToLower(cmds[0]); cmd {
case "up":
log.Println("Up plate device:", cmds[1])
sendCtrlPacket(p, snp, sendCtrl(snp, ctrNON, ctrUP))
case "down":
log.Println("Down plate device:", cmds[1])
sendCtrlPacket(p, snp, sendCtrl(snp, ctrNON, ctlDOWN))
case "on":
log.Println("On Loop device:", cmds[1])
case "off":
log.Println("Off Loop device:", cmds[1])
default:
log.Println("Unknow command")
}
}
}
}()
for {
time.Sleep(4 * time.Second)
/*
for snp := range snpMap {
time.Sleep(20 * time.Millisecond)
//if sendCtrlPacket(p, snp, sendCtrl(snp, ctrNON, ctrUP))
//log.Println("send command" + strconv.Itoa(snp))
}
*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment