Created
March 28, 2020 10:26
-
-
Save KunYi/7ea6f0de39aeedabcd5177ae79b43282 to your computer and use it in GitHub Desktop.
/* for Sanica SNP-400 packing system testing */
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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