123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- package main
- import (
- "context"
- "errors"
- "fmt"
- zgpf_core "git.swzry.com/zry/zry-go-program-framework/core"
- "github.com/tarm/serial"
- "sync"
- )
- var _ zgpf_core.ISubService = (*SerialSubSvc)(nil)
- type SerialSubSvc struct {
- serialCfg *serial.Config
- serialPort *serial.Port
- subSvcContext context.Context
- subSvcCancel context.CancelFunc
- TxChan chan []byte
- RxChan chan []byte
- }
- func NewSerialSubSvc() *SerialSubSvc {
- s := &SerialSubSvc{
- TxChan: make(chan []byte),
- RxChan: make(chan []byte),
- }
- return s
- }
- func (s *SerialSubSvc) Prepare(ctx *zgpf_core.SubServiceContext) error {
- sd, ss, sp, err := GetDnSnP(Config.Serial.DataBits, Config.Serial.StopBits, Config.Serial.Parity)
- if err != nil {
- ctx.Error("invalid serial port config: ", err)
- return errors.New("invalid serial port config")
- }
- s.serialCfg = &serial.Config{
- Name: Config.Serial.ComPort,
- Baud: Config.Serial.Baudrate,
- Size: sd,
- Parity: sp,
- StopBits: ss,
- }
- return nil
- }
- func (s *SerialSubSvc) Run(ctx *zgpf_core.SubServiceContext) error {
- s.subSvcContext, s.subSvcCancel = context.WithCancel(ctx.GetParentContext())
- sport, err := serial.OpenPort(s.serialCfg)
- if err != nil {
- ctx.ErrorF("failed open serial port '%s': %v", s.serialCfg.Name, err)
- return errors.New("failed open serial port")
- }
- s.serialPort = sport
- ctx.InfoF("serial port '%s' opened", s.serialCfg.Name)
- defer s.serialPort.Close()
- wg := sync.WaitGroup{}
- wg.Add(2)
- go func() {
- defer wg.Done()
- readbuf := make([]byte, Config.Performance.RxBufferSize)
- for {
- n, xerr := s.serialPort.Read(readbuf)
- if xerr != nil {
- ctx.Error("error in read serial port: ", xerr)
- return
- }
- s.RxChan <- readbuf[:n]
- }
- }()
- go func() {
- defer wg.Done()
- for {
- select {
- case <-s.subSvcContext.Done():
- return
- case txdata := <-s.TxChan:
- _, xerr := s.serialPort.Write(txdata)
- if xerr != nil {
- ctx.Error("error in write serial port: ", xerr)
- return
- }
- }
- }
- }()
- wg.Wait()
- ctx.Info("serial sub service end.")
- return nil
- }
- func (s *SerialSubSvc) Stop(ctx *zgpf_core.SubServiceContext) {
- if s.subSvcCancel != nil {
- s.subSvcCancel()
- }
- if s.serialPort != nil {
- err := s.serialPort.Close()
- if err != nil {
- ctx.Warn("error in closing serial port: ", err)
- }
- }
- }
- func GetDnSnP(d int, s float32, p string) (byte, serial.StopBits, serial.Parity, error) {
- if d < 0 {
- return 0, 0, 0, fmt.Errorf("invalid databits: value %d is less than 0", d)
- }
- if d > 255 {
- return 0, 0, 0, fmt.Errorf("invalid databits: value %d is more than 255", d)
- }
- sd := byte(d)
- var ss serial.StopBits
- switch s {
- case 1:
- ss = serial.Stop1
- break
- case 1.5:
- ss = serial.Stop1Half
- case 2:
- ss = serial.Stop2
- default:
- return 0, 0, 0, fmt.Errorf("invalid stopbits: %f (should be 1/1.5/2)", s)
- }
- var sp serial.Parity
- switch p {
- case "N":
- sp = serial.ParityNone
- break
- case "O":
- sp = serial.ParityOdd
- break
- case "E":
- sp = serial.ParityEven
- break
- case "M":
- sp = serial.ParityMark
- break
- case "S":
- sp = serial.ParitySpace
- break
- default:
- return 0, 0, 0, fmt.Errorf("invalid parity: '%s' (should be N/O/E/M/S)", p)
- }
- return sd, ss, sp, nil
- }
|