From 5636ebb863fd2fd1b907aaa33fdce95ae5a1213d Mon Sep 17 00:00:00 2001 From: LightDestory Date: Fri, 3 Jan 2025 12:32:00 +0100 Subject: [PATCH] chore: sync for release --- cmd/main.go | 2 +- shared/shared.go | 8 ++ simulator/api.go | 98 +++++++------------- simulator/components/forwarder/api.go | 50 +++++------ simulator/console/console.go | 4 + simulator/simulator.go | 123 +++++++++----------------- 6 files changed, 108 insertions(+), 177 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 360fbc3..cb8e799 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,7 +23,7 @@ func main() { // Check if the verbose flag is set to true, and if so, enable verbose logging. if cfg.Verbose { shared.Verbose = true - log.Println("Verbose logging enabled") + shared.DebugPrint("Verbose mode enabled") } // Create a new simulator controller and repository. simulatorRepository := repo.NewSimulatorRepository() diff --git a/shared/shared.go b/shared/shared.go index 9ed3ac3..a0f989a 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -1,4 +1,12 @@ package shared +import "log" + // Verbose flag var Verbose bool = false + +func DebugPrint(msg string) { + if Verbose { + log.Printf("[DEBUG]: %s", msg) + } +} diff --git a/simulator/api.go b/simulator/api.go index 948f65c..54dcdfa 100644 --- a/simulator/api.go +++ b/simulator/api.go @@ -2,9 +2,9 @@ package simulator import ( "encoding/hex" - "encoding/json" "errors" "fmt" + "github.com/arslab/lwnsimulator/shared" "log" "strings" @@ -24,20 +24,19 @@ import ( ) func GetInstance() *Simulator { - var s Simulator - + shared.DebugPrint("Init new Simulator instance") + // Initial state of the simulator is stopped s.State = util.Stopped - + // Load saved data s.loadData() - + // Initialized the active devices and gateways maps s.ActiveDevices = make(map[int]int) s.ActiveGateways = make(map[int]int) - + // Init Forwarder s.Forwarder = *f.Setup() - + // Attach console s.Console = c.Console{} - return &s } @@ -47,152 +46,115 @@ func (s *Simulator) AddWebSocket(WebSocket *socketio.Conn) { s.SetupConsole() } +// Run starts the simulation environment func (s *Simulator) Run() { - + shared.DebugPrint("Executing Run") s.State = util.Running s.setup() - s.Print("START", nil, util.PrintBoth) - + shared.DebugPrint("Turning ON active components") for _, id := range s.ActiveGateways { s.turnONGateway(id) } - for _, id := range s.ActiveDevices { s.turnONDevice(id) } } +// Stop terminates the simulation environment func (s *Simulator) Stop() { - + shared.DebugPrint("Executing Stop") s.State = util.Stopped s.Resources.ExitGroup.Add(len(s.ActiveGateways) + len(s.ActiveDevices) - s.ComponentsInactiveTmp) - + shared.DebugPrint("Turning OFF active components") for _, id := range s.ActiveGateways { s.Gateways[id].TurnOFF() } - for _, id := range s.ActiveDevices { s.Devices[id].TurnOFF() } - s.Resources.ExitGroup.Wait() - s.saveStatus() - s.Forwarder.Reset() - s.Print("STOPPED", nil, util.PrintBoth) - s.reset() - } +// SaveBridgeAddress stores the bridge address in the simulator struct and saves it to the simulator.json file func (s *Simulator) SaveBridgeAddress(remoteAddr models.AddressIP) error { - + // Store the bridge address in the simulator struct s.BridgeAddress = fmt.Sprintf("%v:%v", remoteAddr.Address, remoteAddr.Port) - pathDir, err := util.GetPath() if err != nil { log.Fatal(err) } - path := pathDir + "/simulator.json" - - bytes, err := json.MarshalIndent(&s, "", "\t") - if err != nil { - log.Fatal(err) - } - - err = util.WriteConfigFile(path, bytes) - if err != nil { - log.Fatal(err) - } - + s.saveComponent(path, &s) s.Print("Gateway Bridge Address saved", nil, util.PrintOnlyConsole) - return nil } +// GetBridgeAddress returns the bridge address stored in the simulator struct func (s *Simulator) GetBridgeAddress() models.AddressIP { - + // Create an empty AddressIP struct with default values var rServer models.AddressIP if s.BridgeAddress == "" { return rServer } - + // Split the bridge address into address and port parts := strings.Split(s.BridgeAddress, ":") - rServer.Address = parts[0] rServer.Port = parts[1] - return rServer } +// GetGateways returns an array of all gateways in the simulator func (s *Simulator) GetGateways() []gw.Gateway { - var gateways []gw.Gateway - for _, g := range s.Gateways { gateways = append(gateways, *g) } - return gateways - } +// GetDevices returns an array of all devices in the simulator func (s *Simulator) GetDevices() []dev.Device { - var devices []dev.Device - for _, d := range s.Devices { devices = append(devices, *d) } - return devices - } +// SetGateway adds or updates a gateway func (s *Simulator) SetGateway(gateway *gw.Gateway, update bool) (int, int, error) { - + shared.DebugPrint(fmt.Sprintf("Adding/Updating Gateway [%s]", gateway.Info.MACAddress.String())) emptyAddr := lorawan.EUI64{0, 0, 0, 0, 0, 0, 0, 0} - + // Check if the MAC address is valid if gateway.Info.MACAddress == emptyAddr { - s.Print("Error: MAC Address invalid", nil, util.PrintOnlyConsole) return codes.CodeErrorAddress, -1, errors.New("Error: MAC Address invalid") - } - - if !update { //new - + // If the gateway is new, assign a new ID + if !update { gateway.Id = s.NextIDGw - s.NextIDGw++ - - } else { - + } else { // If the gateway is being updated, it must be turned off if s.Gateways[gateway.Id].IsOn() { return codes.CodeErrorDeviceActive, -1, errors.New("Gateway is running, unable update") } - } - + // Check if the name is already used code, err := s.searchName(gateway.Info.Name, gateway.Id, true) if err != nil { - s.Print("Name already used", nil, util.PrintOnlyConsole) return code, -1, err - } - + // Check if the name is already used code, err = s.searchAddress(gateway.Info.MACAddress, gateway.Id, true) if err != nil { - s.Print("DevEUI already used", nil, util.PrintOnlyConsole) return code, -1, err - } - if !gateway.Info.TypeGateway { if s.BridgeAddress == "" { @@ -230,7 +192,7 @@ func (s *Simulator) SetGateway(gateway *gw.Gateway, update bool) (int, int, erro delete(s.ActiveGateways, gateway.Id) } } - + s.NextIDGw++ return codes.CodeOK, gateway.Id, nil } diff --git a/simulator/components/forwarder/api.go b/simulator/components/forwarder/api.go index 572cc29..85f404d 100644 --- a/simulator/components/forwarder/api.go +++ b/simulator/components/forwarder/api.go @@ -1,6 +1,8 @@ package forwarder import ( + "fmt" + "github.com/arslab/lwnsimulator/shared" dl "github.com/arslab/lwnsimulator/simulator/components/device/frames/downlink" m "github.com/arslab/lwnsimulator/simulator/components/forwarder/models" "github.com/arslab/lwnsimulator/simulator/resources/communication/buffer" @@ -8,82 +10,71 @@ import ( "github.com/brocaar/lorawan" ) +// Setup initializes the Forwarder by initializing the maps and returning a pointer to the Forwarder func Setup() *Forwarder { - + shared.DebugPrint("Init new Forwarder instance") f := Forwarder{ DevToGw: make(map[lorawan.EUI64]map[lorawan.EUI64]*buffer.BufferUplink), //1[devEUI] 2 [macAddress] GwtoDev: make(map[uint32]map[lorawan.EUI64]map[lorawan.EUI64]*dl.ReceivedDownlink), //1[fre1] 2 [macAddress] 3[devEUI] Devices: make(map[lorawan.EUI64]m.InfoDevice), Gateways: make(map[lorawan.EUI64]m.InfoGateway), } - return &f - } +// AddDevice adds a device to the Forwarder and update the DevToGw map func (f *Forwarder) AddDevice(d m.InfoDevice) { - f.Mutex.Lock() defer f.Mutex.Unlock() - + shared.DebugPrint(fmt.Sprintf("Add device %v to Forwarder", d.DevEUI)) f.Devices[d.DevEUI] = d - inner := make(map[lorawan.EUI64]*buffer.BufferUplink) f.DevToGw[d.DevEUI] = inner - for _, g := range f.Gateways { - if inRange(d, g) { + shared.DebugPrint(fmt.Sprintf("Adding communication link with %s", g.MACAddress)) f.DevToGw[d.DevEUI][g.MACAddress] = g.Buffer } - } - } +// AddGateway adds a gateway to the Forwarder and update the DevToGw map func (f *Forwarder) AddGateway(g m.InfoGateway) { - f.Mutex.Lock() defer f.Mutex.Unlock() - + shared.DebugPrint(fmt.Sprintf("Add/Update gateway %v to Forwarder", g.MACAddress)) f.Gateways[g.MACAddress] = g - for _, d := range f.Devices { - if inRange(d, g) { + shared.DebugPrint(fmt.Sprintf("Adding communication link with %s", d.DevEUI)) f.DevToGw[d.DevEUI][g.MACAddress] = g.Buffer } - } } +// DeleteDevice removes a device from the Forwarder and update the DevToGw map func (f *Forwarder) DeleteDevice(DevEUI lorawan.EUI64) { - f.Mutex.Lock() defer f.Mutex.Unlock() - - for key := range f.DevToGw[DevEUI] { - delete(f.DevToGw[DevEUI], key) - } - + shared.DebugPrint(fmt.Sprintf("Delete device %v from Forwarder", DevEUI)) + clear(f.DevToGw[DevEUI]) delete(f.DevToGw, DevEUI) delete(f.Devices, DevEUI) - } +// DeleteGateway removes a gateway from the Forwarder and update the DevToGw map func (f *Forwarder) DeleteGateway(g m.InfoGateway) { - f.Mutex.Lock() defer f.Mutex.Unlock() - + shared.DebugPrint(fmt.Sprintf("Delete gateway %v from Forwarder", g.MACAddress)) for _, d := range f.Devices { + shared.DebugPrint(fmt.Sprintf("Removing communication link with %s", d.DevEUI)) delete(f.DevToGw[d.DevEUI], g.MACAddress) } - delete(f.Gateways, g.MACAddress) - } +// UpdateDevice updates a device in the Forwarder func (f *Forwarder) UpdateDevice(d m.InfoDevice) { f.AddDevice(d) } @@ -161,6 +152,11 @@ func (f *Forwarder) Downlink(data *lorawan.PHYPayload, freq uint32, macAddress l } +// Reset resets the Forwarder by creating a new instance of the Forwarder func (f *Forwarder) Reset() { - f = Setup() + shared.DebugPrint("Reset Forwarder") + clear(f.DevToGw) + clear(f.GwtoDev) + clear(f.Devices) + clear(f.Gateways) } diff --git a/simulator/console/console.go b/simulator/console/console.go index 69f64f1..51048c5 100644 --- a/simulator/console/console.go +++ b/simulator/console/console.go @@ -6,20 +6,24 @@ import ( socketio "github.com/googollee/go-socket.io" ) +// Console represents a socket connection to send messages to the web terminal type Console struct { WebSocket socketio.Conn } +// PrintLog prints a message to the command line stdout func (c *Console) PrintLog(message string) { log.Println(message) } +// PrintSocket prints a message to the web terminal via a socket connection func (c *Console) PrintSocket(eventName string, data ...interface{}) { if c.WebSocket != nil { c.WebSocket.Emit(eventName, data...) } } +// SetupWebSocket sets the socket connection for the Console func (c *Console) SetupWebSocket(WebSocket *socketio.Conn) { c.WebSocket = *WebSocket } diff --git a/simulator/simulator.go b/simulator/simulator.go index 06230ea..9240ee2 100644 --- a/simulator/simulator.go +++ b/simulator/simulator.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/arslab/lwnsimulator/shared" "log" "time" @@ -21,56 +22,51 @@ import ( // Simulator is a model type Simulator struct { - State uint8 `json:"-"` - Devices map[int]*dev.Device `json:"-"` - ActiveDevices map[int]int `json:"-"` - ActiveGateways map[int]int `json:"-"` - ComponentsInactiveTmp int `json:"-"` - Gateways map[int]*gw.Gateway `json:"-"` - Forwarder f.Forwarder `json:"-"` - NextIDDev int `json:"nextIDDev"` - NextIDGw int `json:"nextIDGw"` - BridgeAddress string `json:"bridgeAddress"` - Resources res.Resources `json:"-"` - Console c.Console `json:"-"` + State uint8 `json:"-"` // Runtime state: Stop, Running + Devices map[int]*dev.Device `json:"-"` // A collection of devices + ActiveDevices map[int]int `json:"-"` // A collection of active devices + ActiveGateways map[int]int `json:"-"` // A collection of active gateways + ComponentsInactiveTmp int `json:"-"` // Number of inactive components + Gateways map[int]*gw.Gateway `json:"-"` // A collection of gateways + Forwarder f.Forwarder `json:"-"` // Forwarder instance used for communication between devices and gateways + NextIDDev int `json:"nextIDDev"` // Next device ID used for creating a new device + NextIDGw int `json:"nextIDGw"` // Next gateway ID used for creating a new gateway + BridgeAddress string `json:"bridgeAddress"` // Bridge address used to connect to a network + Resources res.Resources `json:"-"` // Resources used for managing the simulator + Console c.Console `json:"-"` // Console instance, used for logging in the web terminal } +// setup loads and initializes the simulator maps for gateways and devices. It also initializes the console func (s *Simulator) setup() { s.setupGateways() s.setupDevices() s.SetupConsole() - s.Print("SETUP OK!", nil, util.PrintBoth) } +// setupGateways initializes the gateways by setting their state to Stopped and adding them to the ActiveGateways map if they are active func (s *Simulator) setupGateways() { - for _, g := range s.Gateways { - s.Gateways[g.Id].State = util.Stopped - if g.Info.Active { s.ActiveGateways[g.Id] = g.Id } - } s.Print("Setup gateways OK!", nil, util.PrintOnlySocket) } +// setupDevices initializes the devices by setting their state to Stopped and adding them to the ActiveDevices map if they are active func (s *Simulator) setupDevices() { - for _, d := range s.Devices { - s.Devices[d.Id].State = util.Stopped - if d.Info.Status.Active { s.ActiveDevices[d.Id] = d.Id } - } s.Print("Setup devices OK!", nil, util.PrintOnlySocket) } +// SetupConsole attach the simulator console to devices and gateways func (s *Simulator) SetupConsole() { for _, d := range s.Devices { s.Devices[d.Id].SetConsole(&s.Console) @@ -80,28 +76,24 @@ func (s *Simulator) SetupConsole() { } } +// loadData retrieves the simulator configuration, devices, and gateways from the JSON files by populating the Simulator struct func (s *Simulator) loadData() { - path, err := util.GetPath() if err != nil { log.Fatal(err) } - err = util.RecoverConfigFile(path+"/simulator.json", &s) if err != nil { log.Fatal(err) } - err = util.RecoverConfigFile(path+"/gateways.json", &s.Gateways) if err != nil { log.Fatal(err) } - err = util.RecoverConfigFile(path+"/devices.json", &s.Devices) if err != nil { log.Fatal(err) } - } func (s *Simulator) searchName(Name string, Id int, gwFlag bool) (int, error) { @@ -161,8 +153,9 @@ func (s *Simulator) searchAddress(address lorawan.EUI64, Id int, gwFlag bool) (i return codes.CodeOK, nil } +// saveComponent saves a configuration of the provided interface to a JSON file func (s *Simulator) saveComponent(path string, v interface{}) { - + shared.DebugPrint(fmt.Sprintf("Saving component %s on disk", path)) bytes, err := json.MarshalIndent(&v, "", "\t") if err != nil { log.Fatal(err) @@ -175,55 +168,44 @@ func (s *Simulator) saveComponent(path string, v interface{}) { } +// saveStatus saves the simulator status, devices, and gateways to JSON files func (s *Simulator) saveStatus() { - + shared.DebugPrint("Saving status on disk") pathDir, err := util.GetPath() if err != nil { log.Fatal(err) } - path := pathDir + "/simulator.json" s.saveComponent(path, &s) - path = pathDir + "/devices.json" s.saveComponent(path, &s.Devices) - path = pathDir + "/gateways.json" s.saveComponent(path, &s.Gateways) - s.Print("Status saved", nil, util.PrintOnlyConsole) } +// turnONDevice activates a device by adding it to the Forwarder and turning it on func (s *Simulator) turnONDevice(Id int) { - infoDev := mfw.InfoDevice{ DevEUI: s.Devices[Id].Info.DevEUI, Location: s.Devices[Id].Info.Location, Range: s.Devices[Id].Info.Configuration.Range, } s.Forwarder.AddDevice(infoDev) - s.Devices[Id].Setup(&s.Resources, &s.Forwarder) s.Devices[Id].TurnON() - s.ActiveDevices[Id] = Id - s.Console.PrintSocket(socket.EventResponseCommand, s.Devices[Id].Info.Name+" Turn ON") } +// turnOFFDevice deactivates a device by removing it from the Forwarder and turning it off func (s *Simulator) turnOFFDevice(Id int) { - s.ComponentsInactiveTmp++ s.Resources.ExitGroup.Add(1) - s.Devices[Id].TurnOFF() - s.Forwarder.DeleteDevice(s.Devices[Id].Info.DevEUI) - s.Resources.ExitGroup.Wait() - delete(s.ActiveDevices, Id) s.ComponentsInactiveTmp-- - status := socket.NewStatusDev{ DevEUI: s.Devices[Id].Info.DevEUI, DevAddr: s.Devices[Id].Info.DevAddr, @@ -232,96 +214,75 @@ func (s *Simulator) turnOFFDevice(Id int) { FCntDown: s.Devices[Id].Info.Status.FCntDown, FCnt: s.Devices[Id].Info.Status.DataUplink.FCnt, } - s.Console.PrintSocket(socket.EventSaveStatus, status) - s.Console.PrintSocket(socket.EventResponseCommand, s.Devices[Id].Info.Name+" Turn OFF") } +// turnONGateway activates a gateway by adding it to the Forwarder and turning it on func (s *Simulator) turnONGateway(Id int) { infoGw := mfw.InfoGateway{ MACAddress: s.Gateways[Id].Info.MACAddress, Buffer: &s.Gateways[Id].BufferUplink, Location: s.Gateways[Id].Info.Location, } - s.Forwarder.AddGateway(infoGw) - s.Gateways[Id].Setup(&s.BridgeAddress, &s.Resources, &s.Forwarder) s.Gateways[Id].TurnON() - - s.ActiveGateways[Id] = Id - s.Console.PrintSocket(socket.EventResponseCommand, s.Gateways[Id].Info.Name+" Turn ON") } +// turnOFFGateway deactivates a gateway by removing it from the Forwarder and turning it off func (s *Simulator) turnOFFGateway(Id int) { - s.ComponentsInactiveTmp++ s.Resources.ExitGroup.Add(1) - s.Gateways[Id].TurnOFF() - s.Resources.ExitGroup.Wait() - delete(s.ActiveGateways, Id) s.ComponentsInactiveTmp-- - infoGw := mfw.InfoGateway{ - Buffer: &s.Gateways[Id].BufferUplink, - Location: s.Gateways[Id].Info.Location, + MACAddress: s.Gateways[Id].Info.MACAddress, + Buffer: &s.Gateways[Id].BufferUplink, + Location: s.Gateways[Id].Info.Location, } - s.Forwarder.DeleteGateway(infoGw) - s.Console.PrintSocket(socket.EventResponseCommand, s.Gateways[Id].Info.Name+" Turn OFF") } +// reset removes all devices and gateways from the ActiveDevices and ActiveGateways maps func (s *Simulator) reset() { - - for key := range s.ActiveGateways { - delete(s.ActiveGateways, key) - } - - for key := range s.ActiveDevices { - delete(s.ActiveDevices, key) - } - - s.ActiveDevices = make(map[int]int) - s.ActiveGateways = make(map[int]int) - + shared.DebugPrint("Resetting simulator") + clear(s.ActiveDevices) + clear(s.ActiveGateways) s.Print("Reset", nil, util.PrintOnlyConsole) - } +// Print logs messages to the console and the web terminal based on the printType func (s *Simulator) Print(content string, err error, printType int) { - - now := time.Now() + // Get current time as a timestamp + now := time.Now().Format(time.Stamp) message := "" messageLog := "" event := socket.EventLog - if err == nil { - message = fmt.Sprintf("[ %s ] [SIM]: %s", now.Format(time.Stamp), content) + message = fmt.Sprintf("[ %s ] [SIM]: %s", now, content) messageLog = fmt.Sprintf("[SIM]: %s", content) } else { - message = fmt.Sprintf("[ %s ] [SIM] [ERROR]: %s", now.Format(time.Stamp), err) + message = fmt.Sprintf("[ %s ] [SIM] [ERROR]: %s", now, err) messageLog = fmt.Sprintf("[SIM] [ERROR]: %s", err) event = socket.EventError } - + // Create a new ConsoleLog struct data := socket.ConsoleLog{ Name: "SIM", Msg: message, } - switch printType { - case util.PrintBoth: - s.Console.PrintSocket(event, data) - s.Console.PrintLog(messageLog) case util.PrintOnlySocket: s.Console.PrintSocket(event, data) case util.PrintOnlyConsole: s.Console.PrintLog(messageLog) + default: // util.PrintBoth + s.Console.PrintSocket(event, data) + s.Console.PrintLog(messageLog) } }