This commit is contained in:
huangsimin 2019-11-06 17:51:00 +08:00
commit bd1bb4a03e
8 changed files with 375 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
my_test.cfg
*.db

97
flow.go Normal file
View File

@ -0,0 +1,97 @@
package flow
import (
"log"
"time"
)
// FlowContext 流程的上下文, 用于传递每个流程之间的参数
type FlowContext struct {
RootFlow *Flow
CurrentFlow *FlowNode
CurrentWrite []byte // 8 byte
CurrentRead []byte // 14 byte
}
// FlowNode 流程节点
type FlowNode struct {
Name string
Path string
prev *FlowNode
next *FlowNode
Task func(cxt *FlowContext) int
}
// Write 该写入函数包含了 自动封装日志格式
func (cxt *FlowContext) Write(flag OperatorFlag) {
operator.portWriterLock.Lock()
wbuf := OperatorOption(flag)
operator.port.Write(wbuf)
operator.port.Flush()
cxt.CurrentWrite = wbuf
operator.portWriterLock.Unlock()
if len(wbuf) == 8 {
flowpath := cxt.CurrentFlow.Path + ">" + cxt.CurrentFlow.Name // 路径
_, err := operator.oplog.Exec("insert into operator(ts , rootflow, flowpath, writelog, readlog) values(?,?,?,?,?)", time.Now().Unix(), cxt.RootFlow.Name, flowpath, cxt.CurrentWrite, cxt.CurrentRead)
if err != nil {
log.Println("日志写入错误: ", err)
}
} else {
log.Println("write buffer len is not equal to 8, now is", len(wbuf), "buf: ", wbuf)
}
}
// Sensor 返回传感器数值, 每次调用都是最新的.
func (cxt *FlowContext) Sensor() *Sensor {
buf := make([]byte, 14)
operator.portReaderLock.Lock()
n, err := operator.port.Read(buf)
cxt.CurrentRead = buf
operator.portReaderLock.Unlock()
if err != nil {
log.Println("read bufferr is error:", err)
} else {
if n == 14 {
return NewSensor(buf)
}
//TODO: 断包, 沾包 处理
log.Println("读取长度不等于14, len = ", n)
}
return nil
}
// Flow 流程
type Flow struct {
Name string
Context *FlowContext // 执行流程时候的上下文
Head *FlowNode
Tail *FlowNode
}
// Add 添加
func (flow *Flow) Add(node *FlowNode) {
if flow.Head == nil {
flow.Head = node
flow.Tail = node
return
}
flow.Tail.next = node
flow.Tail = node
if node.next != nil {
panic("tail next is not nil")
}
}

11
go.mod Normal file
View File

@ -0,0 +1,11 @@
module flow
go 1.13
require (
github.com/mattn/go-sqlite3 v1.11.0
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6 // indirect
gopkg.in/ini.v1 v1.50.0
)

21
go.sum Normal file
View File

@ -0,0 +1,21 @@
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6 h1:ZJUmhYTp8GbGC0ViZRc2U+MIYQ8xx9MscsdXnclfIhw=
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/ini.v1 v1.50.0 h1:c/4YI/GUgB7d2yOkxdsQyYDhW67nWrTl6Zyd9vagYmg=
gopkg.in/ini.v1 v1.50.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

4
my.cfg Normal file
View File

@ -0,0 +1,4 @@
[config]
portid = /dev/pts3
baud = 9600
readtimeout = 5

119
operator.go Normal file
View File

@ -0,0 +1,119 @@
package flow
import (
"database/sql"
"encoding/binary"
"log"
"os"
"sync"
"time"
serial "github.com/tarm/serial"
"gopkg.in/ini.v1"
)
// GetOperatorDB 获取 operator 日志
func GetOperatorDB() *sql.DB {
db, err := sql.Open("sqlite3", "./log.db")
if err != nil {
panic(err)
}
_, err = db.Exec("create table if not EXISTS operator ( ts INT, rootflow varchar(255), flowpath text, writelog char(8), readlog char(14))")
if err != nil {
panic(err)
}
return db
}
// OperatorFlag 操作位
type OperatorFlag uint16
const (
UltrasonicPower OperatorFlag = 0b1000000000000000 // UltrasonicPower bit15 超声波电源开关 1开0关
// CYZ-A-X
CirculatingIrrigation OperatorFlag = 0b0100000000000000 // CirculatingIrrigation bit14 循环灌洗水泵 1开0关
UFRecoil OperatorFlag = 0b0010000000000000 // bit13 UF 超滤膜反冲进水阀 1开0关
UFPositive OperatorFlag = 0b0001000000000000 // bit12 UF 超滤膜正冲进水阀 1开0关
// YM-01-X-03
UFTreatedWater OperatorFlag = 0b0000100000000000 // bit11 UF 超滤膜净水出水阀 1开0关
UFRawWater OperatorFlag = 0b0000010000000000 // bit10 UF超滤膜原水进水阀 1开0关
// CirculatingTankWashWater YM-01-X-01
CirculatingTankWashWater OperatorFlag = 0b0000001000000000 // bit9 循环罐洗进水电动球阀 1开0关
UFPositiveFlushingWaterOutlet OperatorFlag = 0b0000000100000000 // bit8 UF超滤膜正冲浓水出口电磁阀 1开0关
// CleaningTankExhaust YV-02-02-1-X-06
CleaningTankExhaust OperatorFlag = 0b0000000010000000 // bit7 清洗罐排气电磁阀 1开0关
DPFCompactCylinderControlB OperatorFlag = 0b0000000001000000 // bit6 DPF压紧气缸控制电磁阀B 1开0关
DPFCompactCylinderControlA OperatorFlag = 0b0000000000100000 // bit5 DPF压紧气缸控制电磁阀A 1开0关
// YV-02-05-1-X-04
CleaningTankDrainingWater OperatorFlag = 0b0000000000010000 // bit4 清洗罐放水阀控制电磁阀 1开0关
GasExplosion OperatorFlag = 0b0000000000001000 // bit3 气爆阀控制电磁阀 1开0关
// YV-02-02-1-X-02
CleaningTankInflation OperatorFlag = 0b0000000000000100 // bit2 清洗罐充气电磁阀 1开0关
CleaningTankSealB OperatorFlag = 0b0000000000000010 // bit1 清洗罐密封圈充气电磁阀B 1开0关
CleaningTankSealA OperatorFlag = 0b0000000000000001 // bit0 清洗罐密封圈充气电磁阀A 1开0关
)
// OperatorOption 操作位设置
func OperatorOption(flag OperatorFlag) []byte {
var buf []byte = make([]byte, 8, 8)
buf[0] = byte(0xaa)
buf[1] = byte(0x55)
binary.BigEndian.PutUint16(buf[2:], uint16(flag))
check := byte(0)
for _, b := range buf {
check += b
}
buf[7] = check
return buf
}
// Operator 日志系统
type Operator struct {
port *serial.Port
portReaderLock *sync.Mutex
portWriterLock *sync.Mutex
oplog *sql.DB
}
var operator *Operator
func init() {
op := &Operator{}
op.portReaderLock = &sync.Mutex{}
op.portWriterLock = &sync.Mutex{}
op.oplog = GetOperatorDB()
var portid string
var baud int
var rtimeout time.Duration
var cfg *ini.File
cfg, err := ini.Load("my_test.cfg")
if err != nil {
log.Println(err)
log.Println("加载配置my.cfg失败, 将使用默认值")
f, err := os.OpenFile("./my_test.cfg", os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
f.WriteString("[config]\nportid = COM1\nbaud = 9600\nreadtimeout = 5")
cfg, err = ini.Load("my_test.cfg")
if err != nil {
panic(err)
}
}
section := cfg.Section("config")
portid = section.Key("portid").MustString("COM1")
baud = section.Key("baud").MustInt(9600)
rtimeout = time.Second * time.Duration(section.Key("readtimeout").MustInt64(5))
port, err := serial.OpenPort(&serial.Config{Name: portid, Baud: baud, ReadTimeout: rtimeout})
if err != nil {
panic(err)
}
op.port = port
}

63
sensor.go Normal file
View File

@ -0,0 +1,63 @@
package flow
import (
"encoding/binary"
"fmt"
"log"
)
// Sensor 传感器
type Sensor struct {
SP01 uint16 // 清洗罐气压传感器 SP-01 罐体排空,值≤0.5(暂定)
SP02 uint16 // 清洗液水箱水位传感器 SP-02 模拟量传感器
LT01 uint16 // 清洗罐满水位传感器 (非接触式水位传感器) LT-01 模拟量传感器
LT02 uint16 // 清洗罐排水传感器 (非接触式水位传感器) LT-02 模拟量传感器
LT03 uint8 // 清洗液水箱满水位传感器 (浮子式水位传感器) LT-03 值为1,清洗液水箱满水位
}
func (sensor *Sensor) String() string {
return fmt.Sprintf("SP01=%d SP02=%d LT01=%d LT02=%d LT03=%d", sensor.SP01, sensor.SP02, sensor.LT01, sensor.LT02, sensor.LT03)
}
func NewSensor(buf []byte) *Sensor {
if len(buf) == 14 {
if buf[0] == byte(0xaa) && buf[1] == byte(0x55) {
sensor := &Sensor{}
sensor.SP01 = binary.BigEndian.Uint16(buf[2:4])
sensor.SP02 = binary.BigEndian.Uint16(buf[4:6])
sensor.LT01 = binary.BigEndian.Uint16(buf[6:8])
sensor.LT02 = binary.BigEndian.Uint16(buf[8:10])
sensor.LT03 = uint8(buf[10])
return sensor
}
}
log.Println(buf, "非标准传感器字节流")
return nil
}
// BytesFromSensor 生成Buf从Sensor
func BytesFromSensor(sensor *Sensor) []byte {
buf := make([]byte, 14)
buf[0] = byte(0xaa)
buf[1] = byte(0x55)
binary.BigEndian.PutUint16(buf[2:4], sensor.SP01)
binary.BigEndian.PutUint16(buf[4:6], sensor.SP02)
binary.BigEndian.PutUint16(buf[6:8], sensor.LT01)
binary.BigEndian.PutUint16(buf[8:10], sensor.LT02)
buf[10] = sensor.LT03
for _, b := range buf[0:13] {
buf[13] += b
}
return buf
}

58
tests/cfg_test.go Normal file
View File

@ -0,0 +1,58 @@
package main
import (
"log"
"os"
"testing"
"time"
"gopkg.in/ini.v1"
)
func TestIntCreate(t *testing.T) {
os.Remove("./my_test.cfg")
time.Sleep(time.Second * 1)
for i := 0; i < 3; i++ {
var portid string
var baud int
var rtimeout time.Duration
var cfg *ini.File
cfg, err := ini.Load("my_test.cfg")
if err != nil {
log.Println(err)
log.Println("加载配置my.cfg失败, 将使用默认值")
f, err := os.OpenFile("./my_test.cfg", os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
f.WriteString("[config]\nportid = COM1\nbaud = 9600\nreadtimeout = 5")
cfg, err = ini.Load("my_test.cfg")
if err != nil {
panic(err)
}
}
section := cfg.Section("config")
portid = section.Key("portid").MustString("COM1")
baud = section.Key("baud").MustInt(9600)
rtimeout = time.Second * time.Duration(section.Key("readtimeout").MustInt64(5))
if portid != "COM1" {
t.Error(`portid != "COM1"`)
}
if baud != 9600 {
t.Error(`baud != 9600`)
}
if rtimeout != time.Second*5 {
t.Error(`rtimeout != time.Second * 5`)
}
}
os.Remove("./my_test.cfg")
}