|
|
@ -0,0 +1,594 @@ |
|
|
|
package main |
|
|
|
|
|
|
|
import ( |
|
|
|
"bytes" |
|
|
|
"crypto/tls" |
|
|
|
"crypto/x509" |
|
|
|
"encoding/json" |
|
|
|
"fmt" |
|
|
|
mqtt "github.com/eclipse/paho.mqtt.golang" |
|
|
|
"github.com/lxn/walk" |
|
|
|
. "github.com/lxn/walk/declarative" |
|
|
|
"github.com/lxn/win" |
|
|
|
"go.bug.st/serial.v1" |
|
|
|
"io/ioutil" |
|
|
|
"log" |
|
|
|
"os" |
|
|
|
"sort" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
) |
|
|
|
|
|
|
|
type DeviceInfo struct { |
|
|
|
ID int64 `json:"id"` |
|
|
|
DevEui string `json:"devEUI"` |
|
|
|
DevAddr string `json:"devAddr"` |
|
|
|
Username string `json:"username"` |
|
|
|
CreateAt string `json:"createAt"` |
|
|
|
} |
|
|
|
|
|
|
|
type Config struct { |
|
|
|
MQTT struct { |
|
|
|
Host string `json:"host"` |
|
|
|
Port int `json:"port"` |
|
|
|
Username string `json:"username"` |
|
|
|
Password string `json:"password"` |
|
|
|
SSL bool `json:"ssl"` |
|
|
|
CACert string `json:"ca_cert"` |
|
|
|
TLSCert string `json:"tls_cert"` |
|
|
|
TLSKey string `json:"tls_key"` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
type CSModel struct { |
|
|
|
walk.TableModelBase |
|
|
|
walk.SorterBase |
|
|
|
sortColumn int |
|
|
|
sortOrder walk.SortOrder |
|
|
|
items []*DeviceInfo |
|
|
|
} |
|
|
|
|
|
|
|
func (m *CSModel) RowCount() int { |
|
|
|
return len(m.items) |
|
|
|
} |
|
|
|
|
|
|
|
func (m *CSModel) Value(row, col int) interface{} { |
|
|
|
item := m.items[row] |
|
|
|
|
|
|
|
switch col { |
|
|
|
case 0: |
|
|
|
return item.ID |
|
|
|
case 1: |
|
|
|
return item.DevEui |
|
|
|
case 2: |
|
|
|
return item.DevAddr |
|
|
|
case 3: |
|
|
|
return item.Username |
|
|
|
case 4: |
|
|
|
return item.CreateAt |
|
|
|
} |
|
|
|
panic("unexpected col") |
|
|
|
} |
|
|
|
|
|
|
|
func (m *CSModel) Sort(col int, order walk.SortOrder) error { |
|
|
|
m.sortColumn, m.sortOrder = col, order |
|
|
|
sort.Stable(m) |
|
|
|
return m.SorterBase.Sort(col, order) |
|
|
|
} |
|
|
|
|
|
|
|
func (m *CSModel) Len() int { |
|
|
|
return len(m.items) |
|
|
|
} |
|
|
|
|
|
|
|
func (m *CSModel) Less(i, j int) bool { |
|
|
|
a, b := m.items[i], m.items[j] |
|
|
|
c := func(ls bool) bool { |
|
|
|
if m.sortOrder == walk.SortAscending { |
|
|
|
return ls |
|
|
|
} |
|
|
|
return !ls |
|
|
|
} |
|
|
|
|
|
|
|
switch m.sortColumn { |
|
|
|
case 0: |
|
|
|
return c(a.ID < b.ID) |
|
|
|
case 1: |
|
|
|
return c(a.DevEui < b.DevEui) |
|
|
|
case 2: |
|
|
|
return c(a.DevAddr < b.DevAddr) |
|
|
|
case 3: |
|
|
|
return c(a.Username < b.Username) |
|
|
|
default: |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (m *CSModel) Swap(i, j int) { |
|
|
|
m.items[i], m.items[j] = m.items[j], m.items[i] |
|
|
|
} |
|
|
|
|
|
|
|
func NewCSModel() *CSModel { |
|
|
|
return new(CSModel) |
|
|
|
} |
|
|
|
|
|
|
|
type CSMainWindow struct { |
|
|
|
*walk.MainWindow |
|
|
|
mqttClient mqtt.Client |
|
|
|
serialPort serial.Port |
|
|
|
model *CSModel |
|
|
|
tv *walk.TableView |
|
|
|
host,username,password*walk.LineEdit |
|
|
|
port *walk.NumberEdit |
|
|
|
connect, disconnect,caConf,send,open,close *walk.PushButton |
|
|
|
ssl *walk.CheckBox |
|
|
|
msg,recvData,sendData *walk.TextEdit |
|
|
|
commPort,baudRate,dataBits,stopBits,parity *walk.ComboBox |
|
|
|
confFileName string |
|
|
|
conf Config |
|
|
|
icon *walk.Icon |
|
|
|
recvDatas,sendDatas string |
|
|
|
ch chan string |
|
|
|
serialOpen bool |
|
|
|
} |
|
|
|
|
|
|
|
func main() { |
|
|
|
mw := &CSMainWindow{model: NewCSModel(),ch:make(chan string,100),serialOpen:false} |
|
|
|
mw.icon,_ = walk.NewIconFromResourceId(3) |
|
|
|
maxWidth := int(win.GetSystemMetrics(win.SM_CXSCREEN)) - 200 |
|
|
|
maxHeight := int(win.GetSystemMetrics(win.SM_CYSCREEN)) - 100 |
|
|
|
err := MainWindow{ |
|
|
|
AssignTo: &mw.MainWindow, |
|
|
|
Title: "DeviceIDCS", |
|
|
|
Icon: mw.icon, |
|
|
|
Size: Size{maxWidth,maxHeight }, |
|
|
|
Layout: VBox{}, |
|
|
|
Children: []Widget{ |
|
|
|
Composite{ |
|
|
|
Layout: HBox{MarginsZero: true}, |
|
|
|
Children: []Widget{ |
|
|
|
Label{Text:"主机/IP:"}, |
|
|
|
LineEdit{AssignTo:&mw.host}, |
|
|
|
Label{Text:"端口:"}, |
|
|
|
NumberEdit{AssignTo:&mw.port,MinSize:Size{Width:50}}, |
|
|
|
Label{Text:"用户名:"}, |
|
|
|
LineEdit{AssignTo:&mw.username}, |
|
|
|
Label{Text:"密码:"}, |
|
|
|
LineEdit{AssignTo:&mw.password,PasswordMode:true}, |
|
|
|
PushButton{Text:"连接", AssignTo: &mw.connect,OnClicked:mw.Connect}, |
|
|
|
PushButton{Text:"断开连接", AssignTo:&mw.disconnect, Enabled:false, OnClicked: mw.Disconnect}, |
|
|
|
CheckBox{Text:"开启SSL/TLS",AssignTo:&mw.ssl,OnClicked:mw.SSL}, |
|
|
|
PushButton{Text:"证书配置",Enabled:false,AssignTo:&mw.caConf,OnClicked: mw.Config}, |
|
|
|
PushButton{Text:"清空数据", OnClicked: mw.Clean}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
Composite{ |
|
|
|
Layout: HBox{}, |
|
|
|
Children: []Widget{ |
|
|
|
TableView{ |
|
|
|
AssignTo: &mw.tv, |
|
|
|
MinSize:Size{Width:maxWidth*3/5}, |
|
|
|
CheckBoxes: true, |
|
|
|
ColumnsOrderable: true, |
|
|
|
MultiSelection: true, |
|
|
|
Columns: []TableViewColumn{ |
|
|
|
{Title: "序号"}, |
|
|
|
{Title: "终端EUI"}, |
|
|
|
{Title: "终端Addr"}, |
|
|
|
{Title: "用户名"}, |
|
|
|
{Title: "创建时间"}, |
|
|
|
}, |
|
|
|
Model: mw.model, |
|
|
|
}, |
|
|
|
Composite{ |
|
|
|
Layout:VBox{}, |
|
|
|
Children:[]Widget{ |
|
|
|
GroupBox{ |
|
|
|
Title:"串口设置", |
|
|
|
Layout:VBox{SpacingZero:true}, |
|
|
|
Children:[]Widget{ |
|
|
|
Composite{ |
|
|
|
Layout: Grid{Columns:4}, |
|
|
|
Children: []Widget{ |
|
|
|
Label{Text:"端口号:"}, |
|
|
|
ComboBox{AssignTo:&mw.commPort}, |
|
|
|
Label{Text:"波特率:"}, |
|
|
|
ComboBox{AssignTo:&mw.baudRate,Model:[]string{"115200","57600","56000","38400","19200","14400","9600"}}, |
|
|
|
Label{Text:"数据位:"}, |
|
|
|
ComboBox{AssignTo:&mw.dataBits,Model:[]string{"8","7","6","5"}}, |
|
|
|
Label{Text:"停止位:"}, |
|
|
|
ComboBox{AssignTo:&mw.stopBits,Model:[]string{"1","1.5","2"}}, |
|
|
|
Label{Text:"奇偶校验:"}, |
|
|
|
ComboBox{AssignTo:&mw.parity,Model:[]string{"None","Odd","Even","Mark","Space"}}, |
|
|
|
HSpacer{}, |
|
|
|
HSpacer{}, |
|
|
|
HSpacer{}, |
|
|
|
HSpacer{}, |
|
|
|
PushButton{Text:"打开串口", AssignTo: &mw.open,OnClicked:mw.Open}, |
|
|
|
PushButton{Text:"关闭串口", AssignTo: &mw.close,Enabled:false,OnClicked:mw.Close1}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
GroupBox{ |
|
|
|
Title:"串口发送", |
|
|
|
Layout:VBox{SpacingZero:true}, |
|
|
|
Children:[]Widget{ |
|
|
|
Composite{ |
|
|
|
Layout:VBox{}, |
|
|
|
Children:[]Widget{ |
|
|
|
Label{Text:"消息"}, |
|
|
|
TextEdit{AssignTo:&mw.msg}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
Composite{ |
|
|
|
Layout:HBox{}, |
|
|
|
Children:[]Widget{ |
|
|
|
HSpacer{}, |
|
|
|
PushButton{Text:"手动发送",AssignTo:&mw.send,OnClicked: mw.SendMsg}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
Composite{ |
|
|
|
Layout: HBox{}, |
|
|
|
Children: []Widget{ |
|
|
|
TextEdit{AssignTo:&mw.recvData,ReadOnly:true,HScroll:true,VScroll:true}, |
|
|
|
TextEdit{AssignTo:&mw.sendData,ReadOnly:true,HScroll:true,VScroll:true}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}.Create() |
|
|
|
if err != nil { |
|
|
|
panic("DeviceIDCS窗口创建失败") |
|
|
|
} |
|
|
|
_ = mw.port.SetValue(1883) |
|
|
|
_ = mw.baudRate.SetCurrentIndex(0) |
|
|
|
_ = mw.dataBits.SetCurrentIndex(0) |
|
|
|
_ = mw.stopBits.SetCurrentIndex(0) |
|
|
|
_ = mw.parity.SetCurrentIndex(0) |
|
|
|
|
|
|
|
go func(mw *CSMainWindow) { |
|
|
|
for{ |
|
|
|
ports, _ := serial.GetPortsList() |
|
|
|
if (len(ports) > 0 && mw.commPort.Model() == nil) || |
|
|
|
(mw.commPort.Model() != nil && len(ports) != len(mw.commPort.Model().([]string))) { |
|
|
|
_ = mw.commPort.SetModel(ports) |
|
|
|
} |
|
|
|
time.Sleep(time.Second) |
|
|
|
} |
|
|
|
}(mw) |
|
|
|
|
|
|
|
dir,_ := os.Getwd() |
|
|
|
mw.confFileName = dir + "/config.json" |
|
|
|
data, err := ioutil.ReadFile(mw.confFileName) |
|
|
|
if err == nil { |
|
|
|
err = json.Unmarshal(data, &mw.conf) |
|
|
|
if err != nil { |
|
|
|
msg := "配置文件格式错误:" + err.Error() |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
} else { |
|
|
|
_ = mw.host.SetText(mw.conf.MQTT.Host) |
|
|
|
_ = mw.port.SetValue(float64(mw.conf.MQTT.Port)) |
|
|
|
_ = mw.username.SetText(mw.conf.MQTT.Username) |
|
|
|
_ = mw.password.SetText(mw.conf.MQTT.Password) |
|
|
|
mw.ssl.SetChecked(mw.conf.MQTT.SSL) |
|
|
|
} |
|
|
|
} |
|
|
|
mw.Run() |
|
|
|
} |
|
|
|
|
|
|
|
func newTLSConfig(cafile, certFile, certKeyFile string) (*tls.Config, error) { |
|
|
|
if cafile == "" && certFile == "" && certKeyFile == "" { |
|
|
|
return nil, nil |
|
|
|
} |
|
|
|
tlsConfig := &tls.Config{} |
|
|
|
// Import trusted certificates from CAfile.pem.
|
|
|
|
if cafile != "" { |
|
|
|
cacert, err := ioutil.ReadFile(cafile) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
certpool := x509.NewCertPool() |
|
|
|
certpool.AppendCertsFromPEM(cacert) |
|
|
|
|
|
|
|
tlsConfig.RootCAs = certpool // RootCAs = certs used to verify server cert.
|
|
|
|
} |
|
|
|
|
|
|
|
// Import certificate and the key
|
|
|
|
if certFile != "" && certKeyFile != "" { |
|
|
|
kp, err := tls.LoadX509KeyPair(certFile, certKeyFile) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
tlsConfig.Certificates = []tls.Certificate{kp} |
|
|
|
} |
|
|
|
|
|
|
|
return tlsConfig, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (mw *CSMainWindow) Connect() { |
|
|
|
if !mw.serialOpen { |
|
|
|
msg := "串口未打开,请先打开串口" |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
return |
|
|
|
} |
|
|
|
go func() { |
|
|
|
if mw.host.Text() != "" && mw.port.Value() > 0 { |
|
|
|
opts := mqtt.NewClientOptions() |
|
|
|
serverAddr := fmt.Sprintf("%s:%d",mw.host.Text(),int(mw.port.Value())) |
|
|
|
if mw.ssl.Checked() { |
|
|
|
if mw.conf.MQTT.CACert != "" { |
|
|
|
serverAddr = fmt.Sprintf("ssl://%s:%d",mw.host.Text(),int(mw.port.Value())) |
|
|
|
tlsconfig, err := newTLSConfig(mw.conf.MQTT.CACert, mw.conf.MQTT.TLSCert, mw.conf.MQTT.TLSKey) |
|
|
|
if err != nil { |
|
|
|
msg := "证书加载错误:" + err.Error() |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
} |
|
|
|
if tlsconfig != nil { |
|
|
|
opts.SetTLSConfig(tlsconfig) |
|
|
|
} |
|
|
|
}else{ |
|
|
|
msg := "证书未配置,服务端单向认证只需配置CA证书,双向认证还需配置客户端证书及秘钥" |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
opts.AddBroker(serverAddr) |
|
|
|
opts.SetUsername(mw.username.Text()) |
|
|
|
opts.SetPassword(mw.password.Text()) |
|
|
|
opts.SetConnectTimeout(5 * time.Second) |
|
|
|
mw.mqttClient = mqtt.NewClient(opts) |
|
|
|
token := mw.mqttClient.Connect() |
|
|
|
if token.Wait() && token.Error() == nil { |
|
|
|
mw.conf.MQTT.Host = mw.host.Text() |
|
|
|
mw.conf.MQTT.Port = int(mw.port.Value()) |
|
|
|
mw.conf.MQTT.Username = mw.username.Text() |
|
|
|
mw.conf.MQTT.Password = mw.password.Text() |
|
|
|
var confData bytes.Buffer |
|
|
|
d,_ := json.Marshal(&mw.conf) |
|
|
|
_ = json.Indent(&confData, d, "", "\t") |
|
|
|
_ = ioutil.WriteFile(mw.confFileName,confData.Bytes(),0644) |
|
|
|
mw.host.SetEnabled(false) |
|
|
|
mw.port.SetEnabled(false) |
|
|
|
mw.username.SetEnabled(false) |
|
|
|
mw.password.SetEnabled(false) |
|
|
|
mw.disconnect.SetEnabled(true) |
|
|
|
mw.connect.SetEnabled(false) |
|
|
|
mw.ssl.SetEnabled(false) |
|
|
|
mw.caConf.SetEnabled(false) |
|
|
|
mw.send.SetEnabled(false) |
|
|
|
mw.msg.SetEnabled(false) |
|
|
|
topic := "device/id" |
|
|
|
token := mw.mqttClient.Subscribe(topic,0, func(client mqtt.Client, message mqtt.Message) { |
|
|
|
var dev DeviceInfo |
|
|
|
_ = json.Unmarshal(message.Payload(), &dev) |
|
|
|
const TIME_LAYOUT = "2006-01-02 15:04:05" |
|
|
|
if lt, err := time.ParseInLocation(time.RFC3339Nano, dev.CreateAt, time.UTC);err == nil { |
|
|
|
dev.CreateAt = lt.Local().Format(TIME_LAYOUT) |
|
|
|
} |
|
|
|
cmds := []string{"AT+DEUI=" + dev.DevEui + "\r\n","AT+DADDR=" + dev.DevAddr + "\r\n"} |
|
|
|
for _,cmd := range cmds{ |
|
|
|
_, err := mw.serialPort.Write([]byte(cmd)) |
|
|
|
if err != nil { |
|
|
|
mw.sendDatas += err.Error() + "\r\n" |
|
|
|
_ = mw.sendData.SetText(mw.sendDatas) |
|
|
|
continue |
|
|
|
} |
|
|
|
mw.sendDatas += cmd |
|
|
|
_ = mw.sendData.SetText(mw.sendDatas) |
|
|
|
mw.ch <- cmd |
|
|
|
} |
|
|
|
|
|
|
|
mw.model.items = append(mw.model.items, &dev) |
|
|
|
mw.model.PublishRowsReset() |
|
|
|
_ = mw.tv.SetSelectedIndexes([]int{}) |
|
|
|
}) |
|
|
|
if token.Wait() && token.Error() != nil { |
|
|
|
msg := "订阅失败:" + token.Error().Error() |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
} |
|
|
|
}else{ |
|
|
|
msg := "连接失败:" + token.Error().Error() |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
} |
|
|
|
}else{ |
|
|
|
msg := "主机/IP不能为空,端口不能为0" |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
} |
|
|
|
}() |
|
|
|
} |
|
|
|
|
|
|
|
func (mw *CSMainWindow) Disconnect() { |
|
|
|
mw.connect.SetEnabled(true) |
|
|
|
mw.host.SetEnabled(true) |
|
|
|
mw.port.SetEnabled(true) |
|
|
|
mw.username.SetEnabled(true) |
|
|
|
mw.password.SetEnabled(true) |
|
|
|
mw.disconnect.SetEnabled(false) |
|
|
|
mw.ssl.SetEnabled(true) |
|
|
|
mw.caConf.SetEnabled(true) |
|
|
|
mw.mqttClient.Disconnect(0) |
|
|
|
mw.send.SetEnabled(true) |
|
|
|
mw.msg.SetEnabled(true) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (mw *CSMainWindow) Clean() { |
|
|
|
mw.model.items = []*DeviceInfo{} |
|
|
|
mw.model.PublishRowsReset() |
|
|
|
_ = mw.tv.SetSelectedIndexes([]int{}) |
|
|
|
} |
|
|
|
func (mw *CSMainWindow) SSL() { |
|
|
|
mw.conf.MQTT.SSL = mw.ssl.Checked() |
|
|
|
if mw.ssl.Checked() { |
|
|
|
mw.caConf.SetEnabled(true) |
|
|
|
}else{ |
|
|
|
mw.caConf.SetEnabled(false) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (mw *CSMainWindow) Config() { |
|
|
|
var dlg *walk.Dialog |
|
|
|
var caCert,tlsCert,tlsKey *walk.LineEdit |
|
|
|
var acceptPB, cancelPB *walk.PushButton |
|
|
|
_ = Dialog{ |
|
|
|
Title: "配置", |
|
|
|
Layout: VBox{}, |
|
|
|
Icon: mw.icon, |
|
|
|
AssignTo: &dlg, |
|
|
|
DefaultButton: &acceptPB, |
|
|
|
CancelButton: &cancelPB, |
|
|
|
MinSize: Size{400, 200}, |
|
|
|
Children: []Widget{ |
|
|
|
Composite{ |
|
|
|
Layout:Grid{Columns: 3}, |
|
|
|
Children:[]Widget{ |
|
|
|
Label{Text:"CA证书:"}, |
|
|
|
LineEdit{Text:Bind("CACert"),AssignTo:&caCert}, |
|
|
|
PushButton{Text:"打开",OnClicked: func() { |
|
|
|
dlg := new(walk.FileDialog) |
|
|
|
dlg.Title = "请选择CA证书" |
|
|
|
dlg.Filter = "CA证书 (*.crt)|*.crt|所有文件 (*.*)|*.*" |
|
|
|
if ok, err := dlg.ShowOpen(mw); err != nil { |
|
|
|
return |
|
|
|
} else if !ok { |
|
|
|
return |
|
|
|
} |
|
|
|
_ = caCert.SetText(dlg.FilePath) |
|
|
|
}}, |
|
|
|
Label{Text:"客户端证书:"}, |
|
|
|
LineEdit{Text:Bind("TLSCert"),AssignTo:&tlsCert}, |
|
|
|
PushButton{Text:"打开",OnClicked: func() { |
|
|
|
dlg := new(walk.FileDialog) |
|
|
|
dlg.Title = "请选择客户端证书" |
|
|
|
dlg.Filter = "客户端证书 (*.crt)|*.crt|所有文件 (*.*)|*.*" |
|
|
|
if ok, err := dlg.ShowOpen(mw); err != nil { |
|
|
|
return |
|
|
|
} else if !ok { |
|
|
|
return |
|
|
|
} |
|
|
|
_ = tlsCert.SetText(dlg.FilePath) |
|
|
|
}}, |
|
|
|
Label{Text:"客户端证书秘钥:"}, |
|
|
|
LineEdit{Text:Bind("TLSKey"),AssignTo:&tlsKey}, |
|
|
|
PushButton{Text:"打开",OnClicked: func() { |
|
|
|
dlg := new(walk.FileDialog) |
|
|
|
dlg.Title = "请选择客户端证书秘钥" |
|
|
|
dlg.Filter = "客户端证书秘钥 (*.key)|*.key|所有文件 (*.*)|*.*" |
|
|
|
if ok, err := dlg.ShowOpen(mw); err != nil { |
|
|
|
return |
|
|
|
} else if !ok { |
|
|
|
return |
|
|
|
} |
|
|
|
_ = tlsKey.SetText(dlg.FilePath) |
|
|
|
}}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
Composite{ |
|
|
|
Layout: HBox{}, |
|
|
|
Children: []Widget{ |
|
|
|
HSpacer{}, |
|
|
|
PushButton{ |
|
|
|
AssignTo: &acceptPB, |
|
|
|
Text: "确定", |
|
|
|
OnClicked: func() { |
|
|
|
mw.conf.MQTT.CACert = caCert.Text() |
|
|
|
mw.conf.MQTT.TLSCert = tlsCert.Text() |
|
|
|
mw.conf.MQTT.TLSKey = tlsKey.Text() |
|
|
|
dlg.Accept() |
|
|
|
}, |
|
|
|
}, |
|
|
|
PushButton{ |
|
|
|
AssignTo: &cancelPB, |
|
|
|
Text: "取消", |
|
|
|
OnClicked: func() { dlg.Cancel() }, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}.Create(mw) |
|
|
|
_ = caCert.SetText(mw.conf.MQTT.CACert) |
|
|
|
_ = tlsCert.SetText(mw.conf.MQTT.TLSCert) |
|
|
|
_ = tlsKey.SetText(mw.conf.MQTT.TLSKey) |
|
|
|
dlg.Run() |
|
|
|
} |
|
|
|
func (mw *CSMainWindow) Open() { |
|
|
|
baudRate,err := strconv.Atoi(mw.baudRate.Text()) |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
dataBits,err := strconv.Atoi(mw.dataBits.Text()) |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
mode := &serial.Mode{ |
|
|
|
BaudRate: baudRate, |
|
|
|
DataBits: dataBits, |
|
|
|
StopBits: serial.StopBits(mw.stopBits.CurrentIndex()), |
|
|
|
Parity: serial.Parity(mw.parity.CurrentIndex()), |
|
|
|
} |
|
|
|
mw.serialPort,err = serial.Open(mw.commPort.Text(),mode) |
|
|
|
if err != nil { |
|
|
|
msg := "打开串口失败:" + err.Error() |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
return |
|
|
|
} |
|
|
|
mw.open.SetEnabled(false) |
|
|
|
mw.close.SetEnabled(true) |
|
|
|
mw.serialOpen = true |
|
|
|
go RecvMsg(mw) |
|
|
|
} |
|
|
|
func (mw *CSMainWindow) Close1() { |
|
|
|
mw.open.SetEnabled(true) |
|
|
|
mw.close.SetEnabled(false) |
|
|
|
_ = mw.serialPort.Close() |
|
|
|
} |
|
|
|
func (mw *CSMainWindow) SendMsg() { |
|
|
|
if mw.serialPort == nil { |
|
|
|
msg := "请先打开串口" |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
return |
|
|
|
} |
|
|
|
msg := mw.msg.Text() + "\r\n" |
|
|
|
_, err := mw.serialPort.Write([]byte(msg)) |
|
|
|
if err != nil { |
|
|
|
msg := "串口写入失败:" + err.Error() |
|
|
|
walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) |
|
|
|
return |
|
|
|
} |
|
|
|
mw.ch <- msg |
|
|
|
mw.sendDatas += msg |
|
|
|
_ = mw.sendData.SetText(mw.sendDatas) |
|
|
|
} |
|
|
|
|
|
|
|
func RecvMsg(mw *CSMainWindow) { |
|
|
|
buff := make([]byte, 100) |
|
|
|
for { |
|
|
|
n, err := mw.serialPort.Read(buff) |
|
|
|
if err != nil { |
|
|
|
log.Println("读串口错误",err) |
|
|
|
break |
|
|
|
} |
|
|
|
msg := string(buff[:n]) |
|
|
|
dim := " ---- " |
|
|
|
cmd := <- mw.ch |
|
|
|
if strings.Contains(msg,"\r\n") { |
|
|
|
msg = strings.Trim(msg,"\r\n") |
|
|
|
} |
|
|
|
if !strings.Contains(cmd,"\r\n") { |
|
|
|
cmd += "\r\n" |
|
|
|
} |
|
|
|
mw.recvDatas += msg + dim + cmd |
|
|
|
_ = mw.recvData.SetText(mw.recvDatas) |
|
|
|
cmd = "" |
|
|
|
if n == 0 { |
|
|
|
fmt.Println("\nEOF") |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
} |