diff --git a/.gitignore b/.gitignore index f3aa750..f32cc05 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,4 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser - +.idea diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..1828dd9 --- /dev/null +++ b/build.bat @@ -0,0 +1,3 @@ +rem set GOARCH=386 +set CGO_ENABLED=0 +go build -ldflags "-H windowsgui" \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..c3d31cf --- /dev/null +++ b/config.json @@ -0,0 +1,12 @@ +{ + "MQTT": { + "host": "39.98.253.192", + "port": 1885, + "username": "", + "password": "", + "ssl": false, + "ca_cert": "", + "tls_cert": "", + "tls_key": "" + } +} \ No newline at end of file diff --git a/deviceIDCS.go b/deviceIDCS.go new file mode 100644 index 0000000..70b6de5 --- /dev/null +++ b/deviceIDCS.go @@ -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 + } + } +} \ No newline at end of file diff --git a/deviceIDCS.ico b/deviceIDCS.ico new file mode 100644 index 0000000..a914fee Binary files /dev/null and b/deviceIDCS.ico differ diff --git a/deviceIDCS.manifest b/deviceIDCS.manifest new file mode 100644 index 0000000..1492a19 --- /dev/null +++ b/deviceIDCS.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + PerMonitorV2, PerMonitor + True + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..97ee7b7 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module deviceIDCS + +go 1.12 + +require ( + github.com/creack/goselect v0.1.1 // indirect + github.com/eclipse/paho.mqtt.golang v1.2.0 + github.com/lxn/walk v0.0.0-20191106172653-8c6cb582c05e + github.com/lxn/win v0.0.0-20191106123917-121afc750dd3 + go.bug.st/serial.v1 v0.0.0-20191202182710-24a6610f0541 + golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect + gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ab851be --- /dev/null +++ b/go.sum @@ -0,0 +1,19 @@ +github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= +github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/lxn/walk v0.0.0-20191106172653-8c6cb582c05e h1:dv4IlLUbRVtjwFx464cMbPrgs+c3WYp1YDkBYADgTaQ= +github.com/lxn/walk v0.0.0-20191106172653-8c6cb582c05e/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20191106123917-121afc750dd3 h1:DvGEvKK/Qnhph/EgdBN9zXA7pEosgJ0k57ojII51JAo= +github.com/lxn/win v0.0.0-20191106123917-121afc750dd3/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= +go.bug.st/serial.v1 v0.0.0-20191202182710-24a6610f0541 h1:eQfoPfT+gNSh63t/oKanQlZyKgblRa/LMZRPIT+MHzA= +go.bug.st/serial.v1 v0.0.0-20191202182710-24a6610f0541/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc= +gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= diff --git a/rsrc.syso b/rsrc.syso new file mode 100644 index 0000000..b5fe3a0 Binary files /dev/null and b/rsrc.syso differ diff --git a/syso.bat b/syso.bat new file mode 100644 index 0000000..b14004d --- /dev/null +++ b/syso.bat @@ -0,0 +1 @@ +rsrc -manifest deviceIDCS.manifest -ico deviceIDCS.ico -o rsrc.syso \ No newline at end of file