From dcfed9c2b321c1baf3102d9141bd98c33ee6d9be Mon Sep 17 00:00:00 2001 From: lihua <531456470@qq.com> Date: Fri, 22 Nov 2019 09:50:21 +0800 Subject: [PATCH] =?UTF-8?q?dtu=20=E6=94=B9=E4=B8=BAmote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LoRaDTUConf.json => LoRaMoteConf.json | 4 +- LoRaDTUMock.go => LoRaMoteMock.go | 208 +++++++++--------- LoRaMoteMock.ico | Bin 0 -> 67646 bytes LoRaDTUMock.json => LoRaMoteMock.json | 0 LoRaDTUMock.manifest => LoRaMoteMock.manifest | 0 dot.ico | Bin 0 -> 67646 bytes go.mod | 2 +- go.sum | 56 +---- jsonModel.go => json_model.go | 2 +- model.go | 28 +-- rsrc.syso | Bin 68863 -> 136623 bytes syso.bat | 2 +- utils.go | 2 +- 13 files changed, 133 insertions(+), 171 deletions(-) rename LoRaDTUConf.json => LoRaMoteConf.json (90%) rename LoRaDTUMock.go => LoRaMoteMock.go (78%) create mode 100644 LoRaMoteMock.ico rename LoRaDTUMock.json => LoRaMoteMock.json (100%) rename LoRaDTUMock.manifest => LoRaMoteMock.manifest (100%) create mode 100644 dot.ico rename jsonModel.go => json_model.go (97%) diff --git a/LoRaDTUConf.json b/LoRaMoteConf.json similarity index 90% rename from LoRaDTUConf.json rename to LoRaMoteConf.json index a829781..ed687fa 100644 --- a/LoRaDTUConf.json +++ b/LoRaMoteConf.json @@ -7,6 +7,6 @@ "appSKey": "2bc9217655160b2c1229b5ecb469242f", "nwkSKey": "4f862983a453cc50336284fd3c62aba5", "fPort": 2, - "fCnt": 1, - "freq": 470.3 + "fCnt": 6, + "freq": 914.7 } \ No newline at end of file diff --git a/LoRaDTUMock.go b/LoRaMoteMock.go similarity index 78% rename from LoRaDTUMock.go rename to LoRaMoteMock.go index dc11ebf..a912bfe 100644 --- a/LoRaDTUMock.go +++ b/LoRaMoteMock.go @@ -1,7 +1,7 @@ package main import ( - "LoRaDTUMock/packets" + "LoRaMoteMock/packets" "bytes" "encoding/hex" "encoding/json" @@ -21,32 +21,34 @@ import ( "time" ) -type DTUMainWindow struct { +type MoteMainWindow struct { *walk.MainWindow - mqttClient paho.Client - model *DTUModel - tv *walk.TableView - jsonView *walk.TreeView - host,username,password *walk.LineEdit - port,sendInterval *walk.NumberEdit - connect, disconnect,caConf,send *walk.PushButton - ascii,noAscii *walk.RadioButton - msg,data *walk.TextEdit - ssl,timeSend *walk.CheckBox - connConf ConnectConfig - dtuConf DTUConfig - connConfFileName string - dtuConfFileName string + mqttClient paho.Client + model *MoteModel + tv *walk.TableView + jsonView *walk.TreeView + host,username,password *walk.LineEdit + port,sendInterval *walk.NumberEdit + connect, disconnect,caConf,send *walk.PushButton + ascii,noAscii *walk.RadioButton + msg,data *walk.TextEdit + ssl,timeSend *walk.CheckBox + connConf ConnectConfig + moteConf MoteConfig + connConfFileName string + moteConfFileName string + icon *walk.Icon } func main() { - mw := &DTUMainWindow{model: NewDTUModel()} + mw := &MoteMainWindow{model: NewMoteModel()} + 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: "LoRaDTUMock", - //Icon: "test.ico", + Title: "LoRaMoteMock", + Icon: mw.icon, Size: Size{maxWidth,maxHeight }, Layout: VBox{}, Children: []Widget{ @@ -65,7 +67,7 @@ func main() { 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.ConnectConfig}, - PushButton{Text:"终端配置",OnClicked: mw.DTUConfig}, + PushButton{Text:"终端配置",OnClicked: mw.MoteConfig}, PushButton{Text:"清空数据",OnClicked: mw.Clean}, }, }, @@ -140,13 +142,13 @@ func main() { }, }.Create() if err != nil { - panic("LoRaDTUMock窗口创建失败") + panic("LoRaMoteMock窗口创建失败") } _ = mw.port.SetValue(1883) _ = mw.sendInterval.SetValue(1000) mw.ascii.SetChecked(true) dir,_ := os.Getwd() - mw.connConfFileName = dir + "/LoRaDTUMock.json" + mw.connConfFileName = dir + "/LoRaMoteMock.json" data, err := ioutil.ReadFile(mw.connConfFileName) if err == nil { err = json.Unmarshal(data, &mw.connConf) @@ -160,10 +162,10 @@ func main() { _ = mw.username.SetText(mw.connConf.Username) _ = mw.password.SetText(mw.connConf.Password) } - mw.dtuConfFileName = dir + "/LoRaDTUConf.json" - data, err = ioutil.ReadFile(mw.dtuConfFileName) + mw.moteConfFileName = dir + "/LoRaMoteConf.json" + data, err = ioutil.ReadFile(mw.moteConfFileName) if err == nil { - err = json.Unmarshal(data, &mw.dtuConf) + err = json.Unmarshal(data, &mw.moteConf) if err != nil { msg := "配置文件格式错误:" + err.Error() walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) @@ -174,7 +176,7 @@ func main() { } -func (mw *DTUMainWindow) Connect() { +func (mw *MoteMainWindow) Connect() { go func() { if mw.host.Text() != "" && mw.port.Value() > 0 { opts := paho.NewClientOptions() @@ -235,7 +237,7 @@ func (mw *DTUMainWindow) Connect() { }() } -func (mw *DTUMainWindow) Disconnect() { +func (mw *MoteMainWindow) Disconnect() { mw.connect.SetEnabled(true) mw.host.SetEnabled(true) mw.port.SetEnabled(true) @@ -246,14 +248,15 @@ func (mw *DTUMainWindow) Disconnect() { mw.mqttClient.Disconnect(0) } -func (mw *DTUMainWindow) DTUConfig() { +func (mw *MoteMainWindow) MoteConfig() { var dlg *walk.Dialog var otaa *walk.CheckBox var gatewayId,devEUI,devAddr,appKey,appSKey,nwkSKey *walk.LineEdit var fPort,fCnt,freq *walk.NumberEdit var acceptPB, cancelPB *walk.PushButton _ = Dialog{ - Title: "DTU配置", + Title: "终端配置", + Icon: mw.icon, Layout: VBox{}, AssignTo: &dlg, DefaultButton: &acceptPB, @@ -306,21 +309,21 @@ func (mw *DTUMainWindow) DTUConfig() { AssignTo: &acceptPB, Text: "确定", OnClicked: func() { - mw.dtuConf.OTAA = otaa.Checked() - mw.dtuConf.GatewayId = gatewayId.Text() - mw.dtuConf.DevEui = devEUI.Text() - mw.dtuConf.DevAddr = devAddr.Text() - mw.dtuConf.AppKey = appKey.Text() - mw.dtuConf.AppSKey = appSKey.Text() - mw.dtuConf.NwkSKey = nwkSKey.Text() - mw.dtuConf.FPort = uint8(fPort.Value()) - mw.dtuConf.FCnt = uint32(fCnt.Value()) - mw.dtuConf.Freq = freq.Value() + mw.moteConf.OTAA = otaa.Checked() + mw.moteConf.GatewayId = gatewayId.Text() + mw.moteConf.DevEui = devEUI.Text() + mw.moteConf.DevAddr = devAddr.Text() + mw.moteConf.AppKey = appKey.Text() + mw.moteConf.AppSKey = appSKey.Text() + mw.moteConf.NwkSKey = nwkSKey.Text() + mw.moteConf.FPort = uint8(fPort.Value()) + mw.moteConf.FCnt = uint32(fCnt.Value()) + mw.moteConf.Freq = freq.Value() var confData bytes.Buffer - d,_ := json.Marshal(&mw.dtuConf) + d,_ := json.Marshal(&mw.moteConf) _ = json.Indent(&confData, d, "", "\t") - _ = ioutil.WriteFile(mw.dtuConfFileName,confData.Bytes(),0644) + _ = ioutil.WriteFile(mw.moteConfFileName,confData.Bytes(),0644) dlg.Accept() }, }, @@ -334,25 +337,25 @@ func (mw *DTUMainWindow) DTUConfig() { }, }.Create(mw) - otaa.SetChecked(mw.dtuConf.OTAA) - _ = gatewayId.SetText(mw.dtuConf.GatewayId) - _ = devEUI.SetText(mw.dtuConf.DevEui) - _ = devAddr.SetText(mw.dtuConf.DevAddr) - _ = appKey.SetText(mw.dtuConf.AppKey) - _ = appSKey.SetText(mw.dtuConf.AppSKey) - _ = nwkSKey.SetText(mw.dtuConf.NwkSKey) - _ = fPort.SetValue(float64(mw.dtuConf.FPort)) - _ = fCnt.SetValue(float64(mw.dtuConf.FCnt)) - _ = freq.SetValue(mw.dtuConf.Freq) + otaa.SetChecked(mw.moteConf.OTAA) + _ = gatewayId.SetText(mw.moteConf.GatewayId) + _ = devEUI.SetText(mw.moteConf.DevEui) + _ = devAddr.SetText(mw.moteConf.DevAddr) + _ = appKey.SetText(mw.moteConf.AppKey) + _ = appSKey.SetText(mw.moteConf.AppSKey) + _ = nwkSKey.SetText(mw.moteConf.NwkSKey) + _ = fPort.SetValue(float64(mw.moteConf.FPort)) + _ = fCnt.SetValue(float64(mw.moteConf.FCnt)) + _ = freq.SetValue(mw.moteConf.Freq) dlg.Run() } -func (mw *DTUMainWindow) Clean() { - mw.model.Items = []*DTU{} +func (mw *MoteMainWindow) Clean() { + mw.model.Items = []*Mote{} mw.model.PublishRowsReset() _ = mw.tv.SetSelectedIndexes([]int{}) } -func (mw *DTUMainWindow) SSL() { +func (mw *MoteMainWindow) SSL() { if mw.ssl.Checked() { mw.caConf.SetEnabled(true) }else{ @@ -360,12 +363,13 @@ func (mw *DTUMainWindow) SSL() { } } -func (mw *DTUMainWindow) ConnectConfig() { +func (mw *MoteMainWindow) ConnectConfig() { var dlg *walk.Dialog var caCert,tlsCert,tlsKey *walk.LineEdit var acceptPB, cancelPB *walk.PushButton _ = Dialog{ Title: "连接配置", + Icon: mw.icon, Layout: VBox{}, AssignTo: &dlg, DefaultButton: &acceptPB, @@ -445,7 +449,7 @@ func (mw *DTUMainWindow) ConnectConfig() { dlg.Run() } -func (mw *DTUMainWindow) HandleData(client paho.Client, message paho.Message){ +func (mw *MoteMainWindow) HandleData(client paho.Client, message paho.Message){ var downlinkFrame gw.DownlinkFrame err := proto.Unmarshal(message.Payload(),&downlinkFrame) if err == nil { @@ -465,11 +469,11 @@ func (mw *DTUMainWindow) HandleData(client paho.Client, message paho.Message){ var origData bytes.Buffer jsonData,_ := phy.MarshalJSON() _ = json.Indent(&origData, jsonData, "", " ") - dd := &DTU{ + dd := &Mote{ Index: mw.model.Len() + 1, Direction:"downlink", - DevEUI: mw.dtuConf.DevEui, - DevAddr: mw.dtuConf.DevAddr, + DevEUI: mw.moteConf.DevEui, + DevAddr: mw.moteConf.DevAddr, MType: phy.MHDR.MType.String(), GatewayID: hex.EncodeToString(downlinkFrame.TxInfo.GatewayId), Time:time.Now().Format("2006-01-02 15:04:05"), @@ -495,8 +499,8 @@ func (mw *DTUMainWindow) HandleData(client paho.Client, message paho.Message){ } } -func (mw *DTUMainWindow) HandleJoinAccept(phy *lorawan.PHYPayload){ - key := mw.dtuConf.AppKey +func (mw *MoteMainWindow) HandleJoinAccept(phy *lorawan.PHYPayload){ + key := mw.moteConf.AppKey var aseKey lorawan.AES128Key _ = aseKey.UnmarshalText([]byte(key)) err := phy.DecryptJoinAcceptPayload(aseKey) @@ -507,36 +511,36 @@ func (mw *DTUMainWindow) HandleJoinAccept(phy *lorawan.PHYPayload){ if !ok { fmt.Println("lorawan: MACPayload must be of type *JoinAcceptPayload") } - mw.dtuConf.DevAddr = jap.DevAddr.String() - dn := mw.dtuConf.devNonce + mw.moteConf.DevAddr = jap.DevAddr.String() + dn := mw.moteConf.devNonce appSKey,err := getAppSKey(aseKey,jap.HomeNetID,jap.JoinNonce,dn) if err == nil { - mw.dtuConf.AppSKey = appSKey.String() - fmt.Println(mw.dtuConf.AppSKey) + mw.moteConf.AppSKey = appSKey.String() + fmt.Println(mw.moteConf.AppSKey) } nwkSKey,err := getNwkSKey(aseKey,jap.HomeNetID,jap.JoinNonce,dn) if err == nil { - mw.dtuConf.NwkSKey = nwkSKey.String() - fmt.Println(mw.dtuConf.NwkSKey) + mw.moteConf.NwkSKey = nwkSKey.String() + fmt.Println(mw.moteConf.NwkSKey) } } -func (mw *DTUMainWindow) HandleDataDown(phy *lorawan.PHYPayload){ +func (mw *MoteMainWindow) HandleDataDown(phy *lorawan.PHYPayload){ mpl := phy.MACPayload.(*lorawan.MACPayload) - key := mw.dtuConf.AppSKey + key := mw.moteConf.AppSKey if mpl.FPort != nil && *mpl.FPort == 0 { - key = mw.dtuConf.NwkSKey + key = mw.moteConf.NwkSKey } var aseKey lorawan.AES128Key _ = aseKey.UnmarshalText([]byte(key)) err := phy.DecryptFRMPayload(aseKey) if err == nil { - mw.dtuConf.DevAddr = mpl.FHDR.DevAddr.String() + mw.moteConf.DevAddr = mpl.FHDR.DevAddr.String() } } -func (mw *DTUMainWindow) PushData(gatewayEUI string,event string, msg proto.Message) { +func (mw *MoteMainWindow) PushData(gatewayEUI string,event string, msg proto.Message) { topic := fmt.Sprintf("gateway/%s/event/%s",gatewayEUI,event) b, err := proto.Marshal(msg) if err != nil { @@ -549,26 +553,26 @@ func (mw *DTUMainWindow) PushData(gatewayEUI string,event string, msg proto.Mess fmt.Println("mqtt message error") } } -func (mw *DTUMainWindow) sendMsg() error{ +func (mw *MoteMainWindow) sendMsg() error{ if mw.mqttClient == nil || !mw.mqttClient.IsConnected() { msg := "请先连接服务器" walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) return errors.New("请先连接服务器") } - if mw.dtuConf.OTAA && mw.dtuConf.DevAddr == ""{ - mw.dtuConf.devNonce = lorawan.DevNonce(rand.Uint32()) + if mw.moteConf.OTAA && mw.moteConf.DevAddr == ""{ + mw.moteConf.devNonce = lorawan.DevNonce(rand.Uint32()) appEui := "0807060504030201" - packet,phy,_ := BuildJoin(mw.dtuConf.GatewayId,appEui,mw.dtuConf.DevEui,mw.dtuConf.AppKey, - 5,2,mw.dtuConf.Freq,7,-51, mw.dtuConf.devNonce) + packet,phy,_ := BuildJoin(mw.moteConf.GatewayId,appEui,mw.moteConf.DevEui,mw.moteConf.AppKey, + 5,2,mw.moteConf.Freq,7,-51, mw.moteConf.devNonce) var origData bytes.Buffer jsonData,_ := phy.MarshalJSON() _ = json.Indent(&origData, jsonData, "", " ") - du := &DTU{ + du := &Mote{ Index:mw.model.Len() + 1, Direction:"uplink", - DevEUI:mw.dtuConf.DevEui, + DevEUI:mw.moteConf.DevEui, MType:phy.MHDR.MType.String(), - GatewayID:mw.dtuConf.GatewayId, + GatewayID:mw.moteConf.GatewayId, Rssi:packet.Payload.RXPK[0].RSSI, LoRaSNR:packet.Payload.RXPK[0].LSNR, Frequency:packet.Payload.RXPK[0].Freq, @@ -580,13 +584,13 @@ func (mw *DTUMainWindow) sendMsg() error{ _ = mw.tv.SetSelectedIndexes([]int{}) frames,_:= packet.GetUplinkFrames(true,false) for j := range frames { - mw.PushData(mw.dtuConf.GatewayId,"up",&frames[j]) + mw.PushData(mw.moteConf.GatewayId,"up",&frames[j]) } fmt.Println("push join ") - for cnt := 0;mw.dtuConf.DevAddr == "" && cnt < 5;cnt ++ { + for cnt := 0;mw.moteConf.DevAddr == "" && cnt < 5;cnt ++ { time.Sleep(time.Second) } - if mw.dtuConf.DevAddr != "" { + if mw.moteConf.DevAddr != "" { fmt.Println("join ok") }else{ fmt.Println("join failed") @@ -607,29 +611,29 @@ func (mw *DTUMainWindow) sendMsg() error{ } var fCtrl lorawan.FCtrl _ = fCtrl.UnmarshalBinary([]byte{128}) - key := mw.dtuConf.AppSKey - if mw.dtuConf.FPort == 0 { - key = mw.dtuConf.NwkSKey + key := mw.moteConf.AppSKey + if mw.moteConf.FPort == 0 { + key = mw.moteConf.NwkSKey } - packet,phy,_ := BuildUpData(mw.dtuConf.GatewayId,mw.dtuConf.DevAddr,key, - mw.dtuConf.NwkSKey,mw.dtuConf.FCnt,mw.dtuConf.FPort,5,2,mw.dtuConf.Freq,7, + packet,phy,_ := BuildUpData(mw.moteConf.GatewayId,mw.moteConf.DevAddr,key, + mw.moteConf.NwkSKey,mw.moteConf.FCnt,mw.moteConf.FPort,5,2,mw.moteConf.Freq,7, lorawan.UnconfirmedDataUp,fCtrl,-51,bmsg) var origData bytes.Buffer jsonData,_ := phy.MarshalJSON() _ = json.Indent(&origData, jsonData, "", " ") - du := &DTU{ + du := &Mote{ Index:mw.model.Len() + 1, Direction:"uplink", - DevEUI:mw.dtuConf.DevEui, - DevAddr:mw.dtuConf.DevAddr, + DevEUI:mw.moteConf.DevEui, + DevAddr:mw.moteConf.DevAddr, MType:phy.MHDR.MType.String(), - GatewayID:mw.dtuConf.GatewayId, + GatewayID:mw.moteConf.GatewayId, Rssi:packet.Payload.RXPK[0].RSSI, LoRaSNR:packet.Payload.RXPK[0].LSNR, Frequency:packet.Payload.RXPK[0].Freq, - FCnt:mw.dtuConf.FCnt, - FPort:mw.dtuConf.FPort, + FCnt:mw.moteConf.FCnt, + FPort:mw.moteConf.FPort, HexData:hex.EncodeToString(bmsg), AsciiData:BytesToString(bmsg), Time:time.Now().Format("2006-01-02 15:04:05"), @@ -640,20 +644,20 @@ func (mw *DTUMainWindow) sendMsg() error{ _ = mw.tv.SetSelectedIndexes([]int{}) frames,_:= packet.GetUplinkFrames(true,false) for j := range frames { - mw.PushData(mw.dtuConf.GatewayId,"up",&frames[j]) + mw.PushData(mw.moteConf.GatewayId,"up",&frames[j]) } - mw.dtuConf.FCnt ++ + mw.moteConf.FCnt ++ var confData bytes.Buffer - d,_ := json.Marshal(&mw.dtuConf) + d,_ := json.Marshal(&mw.moteConf) _ = json.Indent(&confData, d, "", "\t") - _ = ioutil.WriteFile(mw.dtuConfFileName,confData.Bytes(),0644) + _ = ioutil.WriteFile(mw.moteConfFileName,confData.Bytes(),0644) return nil } -func (mw *DTUMainWindow) SendMsg() { +func (mw *MoteMainWindow) SendMsg() { go mw.sendMsg() } -func (mw *DTUMainWindow) SetSend() { +func (mw *MoteMainWindow) SetSend() { if mw.timeSend.Checked() { mw.ascii.SetEnabled(false) mw.noAscii.SetEnabled(false) @@ -669,7 +673,7 @@ func (mw *DTUMainWindow) SetSend() { } } -func (mw *DTUMainWindow) TimeSend() { +func (mw *MoteMainWindow) TimeSend() { if mw.sendInterval.Value() <= 0 { msg := "时间间隔需大于0" walk.MsgBox(mw, "错误", msg, walk.MsgBoxIconError) @@ -690,7 +694,7 @@ func (mw *DTUMainWindow) TimeSend() { } }() } -func (mw *DTUMainWindow) tvItemActivated() { +func (mw *MoteMainWindow) tvItemActivated() { msg := "" for _, i := range mw.tv.SelectedIndexes() { msg += mw.model.Items[i].OrigData + "\n" diff --git a/LoRaMoteMock.ico b/LoRaMoteMock.ico new file mode 100644 index 0000000000000000000000000000000000000000..79790cbca8212e07c8164e63d16b2c48a05226fd GIT binary patch literal 67646 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 J8keSd_WyJJ*Zlwh literal 0 HcmV?d00001 diff --git a/LoRaDTUMock.json b/LoRaMoteMock.json similarity index 100% rename from LoRaDTUMock.json rename to LoRaMoteMock.json diff --git a/LoRaDTUMock.manifest b/LoRaMoteMock.manifest similarity index 100% rename from LoRaDTUMock.manifest rename to LoRaMoteMock.manifest diff --git a/dot.ico b/dot.ico new file mode 100644 index 0000000000000000000000000000000000000000..f24a7b1083e8de81877bc6786d9da19c1303d9bb GIT binary patch literal 67646 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 Q>G7Z_nlWMl?{fV6|7&Y*8UO$Q delta 213 zcmZ3#n&baW7H%I#Mg|ag$-_92TT-x}hmpYs$UP0DL4bLpsWN8-14DxZkkc`-QJ9qp z$Z(k0C@#4JB##0Z8JL0m0+8eh6bTTA5eOL=762vHZvYuDfLMbAVhNDRAi&AUAOd1Q xfeI(%=5j_|?(Gja8NWnsKaj_$v3UC*L8feGF_^{d3@}^R8E!C4pD4;C2LL