I have a piece of code that receives a JSON and creates a instance of a struct depending on it's deviceID.
type Ctrl struct {
Instance []*VD
}
var device *VD
if integrationResult == "successful"{
if len(sensorList.Instance) == 0 {
device = VirtualDevice(client, deviceID)
oldDeviceID = deviceID
sensorList.Instance = append(sensorList.Instance, device)
} else if oldDeviceID != deviceID{
device = VirtualDevice(client, deviceID)
sensorList.Instance = append(sensorList.Instance, device)
}
fmt.Println(*sensorList.Instance[0]) //nothing is in here
}
In another file I have:
type Device struct{
Type string `json:"type"`
Value []interface{} `json:"value"`
CaptureTime string `json:"capture-time"`
}
type VD struct {
Passport struct {
MessageTopic string `json:"message-topic"`
PrivateKey string `json:"private-key"`
} `json:"passport"`
Data struct {
Sensor []Device `json:"sensor"`
Actuator struct {
} `json:"actuator"`
} `json:"data"`
}
func VirtualDevice(client MQTT.Client, deviceID string) *VD {
sensorData := new(VD)
var g MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
err := json.Unmarshal(msg.Payload(), &sensorData)
if err != nil {
panic(err)
} else {
//fmt.Printf("%+v\n", *sensorData) //data_update
}
}
client.Subscribe("data-update/" + deviceID, 0, g)
return sensorData
}
The issue that I have is that *sensorList.Instance[0] prints out an empty JSON. Why is this the case?
You're not waiting for sensorData to actually be filled with data before you return it, so you're returning an empty structure. You can wait for it with
token := client.Subscribe("data-update/" + deviceID, 0, g)
token.wait()
if token.Error() != nil {
// do something useful here
}
return sensorData
You can also use WaitTimeout which lets you specify a time.Duration which is the maximum time you will wait for the data before giving up.
Related
I'm trying to modify the value of a nested struct's variable in Go. Basically, I want to modify the RsvpString property but GetRsvp() seems to return the value of Rsvp instead of a reference, so when I modify its property value, it doesn't get reflected in the Event instance.
The test is below.
type Event struct {
Rsvps []Rsvp `json:"rsvps"`
}
type Rsvp struct {
UserId string `json:"userId"`
RsvpString string `json:"rsvp"`
}
func (e *Event) GetRsvp(userId string) (rsvp *Rsvp, err error) {
for _, element := range e.Rsvps {
if element.UserId == userId {
return &element, nil
}
}
return &Rsvp{}, fmt.Errorf("could not find RSVP based on UserID")
}
func (e *Event) UpdateExistingRsvp(userId string, rsvpString string) {
rsvp, err := e.GetRsvp(userId)
if err == nil {
rsvp.RsvpString = rsvpString
}
}
Here's the test code:
func TestEvent_UpdateExistingRsvp(t *testing.T) {
e := Event{[]Rsvp{
{Name: "Bill",
UserId: "bill",
Rsvp: "yes"}}}
e.UpdateExistingRsvp("bill", "no")
assert.Equal(t, "no", e.Rsvps[0].Rsvp, "RSVP should be switched to no") // fails
}
GetRsvp is returning the address of the loop variable, not the address of the element in the array. To fix:
for i, element := range e.Rsvps {
if element.UserId == userId {
return &e.Rsvps[i], nil
}
}
The loop variable keeps a copy of e.Rsvps[i], and it gets overwritten at every iteration. If you return the address of the loop variable, then you return the address of that copy.
When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
so technically you are trying to modify the copy of the Rsvp.
instead, return the index and from the GetRsvp() method and update.
func (e *Event) GetRsvp(userId string) (int, error) {
for index , element := range e.Rsvps {
if element.UserId == userId {
return index, nil
}
}
return -1 , fmt.Errorf("could not find RSVP based on UserID")
}
func (e *Event) UpdateExistingRsvp(userId string, rsvpString string) {
index, err := e.GetRsvp(userId)
if err != nil || index == -1 {
fmt.Println("no such user")
}
e.Rsvps[index].RsvpString = rsvpString
}
I'm using the following code to get an object from a Firebase realtime database.
type Item struct {
title string `json:"title"`
}
var item Item
if err := db.NewRef("/items/itemid").Get(ctx, &item); err != nil {
log.Infof(ctx, "An error occured %v", err.Error())
}
log.Infof(ctx, "Item %v", item)
If no data exists at the given path in the realtime database the SDK will not return an error, instead I will end up with an empty struct in the variable item.
What would be the cleanest/most readable way to detect that the data at the path is not there?
I've searched for hours but couldn't find a clear cut answer to this question.
Here's one way to solve this problem:
type NullableItem struct {
Item struct {
Title string `json:"title"`
}
IsNull bool
}
func (i *NullableItem) UnmarshalJSON(b []byte) error {
if string(b) == "null" {
i.IsNull = true
return nil
}
return json.Unmarshal(b, &i.Item)
}
func TestGetNonExisting(t *testing.T) {
var i NullableItem
r := client.NewRef("items/non_existing")
if err := r.Get(context.Background(), &i); err != nil {
t.Fatal(err)
}
if !i.IsNull {
t.Errorf("Get() = %v; want IsNull = true", i)
}
}
As a best practice you should also implement MarshalJSON() function.
I am getting data from source A and storing it in a slice of structs like so:
type ProductPrice struct {
Type string
Sku string
UnitPrice string
PriceList string
standardPrice string
specialPrice string
specialStart string
specialEnd string
pricingUnit string
categoryCode string
isOnSpecial bool
}
func getProductPricesFromDatabase(instance string) []ProductPrice {
rows, err := myDBConnection.Query(// My query here)
if err != nil {
log.Fatal("There was an issue with the query for product price: ", err)
}
defer rows.Close()
var productPrices []ProductPrice
for rows.Next() {
var product = ProductPrice{}
err := rows.Scan(
&product.Type,
&product.Sku,
&product.standardPrice,
&product.specialPrice,
&product.specialStart,
&product.specialEnd,
&product.pricingUnit,
&product.PriceList,
&product.categoryCode,
)
if err != nil {
log.Fatal("product price scan error: ", err)
}
productPrices = append(productPrices, product)
}
return productPrices
}
I am then getting some data from source B and storing it in a slice of structs like so:
type ContractProductPrice struct {
CustID string
PriceBy string
AppliesTo string
PriceList string
StartDate string
EndDate string
PricingAdjustmentType string
PricingAdjustmentValue string
UseLowest string
}
func getContractProductPricesFromDatabase(instance string) []ContractProductPrice {
rows, err := myDBConnection.Query(// My query here)
if err != nil {
log.Fatal("There was an issue with the query for contract product price: ", err)
}
defer rows.Close()
var contractProductPrices []ContractProductPrice
for rows.Next() {
var product = ContractProductPrice{}
err := rows.Scan(
&product.CustID,
&product.PriceBy,
&product.AppliesTo,
&product.PriceList,
&product.StartDate,
&product.EndDate,
&product.PricingAdjustmentType,
&product.PricingAdjustmentValue,
&product.UseLowest,
)
if err != nil {
log.Fatal("contract product price scan error: ", err)
}
contractProductPrices = append(contractProductPrices, product)
}
return contractProductPrices
}
After getting the data from source B, I am wanting to update the slice of structs from source A with some data from source B.
productPrices := getProductPricesFromDatabase(instance)
contractProductPrices := getContractProductPricesFromDatabase(instance)
processedProductPrices := processProductPricesFromDatabase(productPrices, contractProductPrices)
func processProductPricesFromDatabase(productPrices []ProductPrice, contractProductPrices []ContractProductPrice) []ProductPrice {
// Loop over contact prices and update relevant product prices
for _, contractPrice := range contractProductPrices {
for _, product := range productPrices {
if contractPrice.AppliesTo == product.Sku {
product.UnitPrice = contractPrice.PricingAdjustmentValue
}
}
}
return productPrices
}
However, after this runs, the unit prices in processedProductPrices is still empty.
From my searching, I understand what the issue is; Go passes by value and so I am not updating the original memory address and so the values are not changing.
However, I do not understand/know what I need to change to fix this given I am working with a slice of structs rather than a simpler example of a slice of number/strings etc.
How can I update productPrices so that when I return it, processedProductPrices is equal to the updated productPrices slice of structs?
Anytime you're dealing with values that you know you'll need to modify, it is best, at least in my opinion, to use pointers. They'll make your life easier.
So instead of:
func getProductPricesFromDatabase(instance string) []ProductPrice {
// ...
var productPrices []ProductPrice
for rows.Next() {
var product = ProductPrice{}
// ...
}
return productPrices
}
I would recommend you refactor your code to:
func getProductPricesFromDatabase(instance string) []*ProductPrice {
// ...
var productPrices []*ProductPrice
for rows.Next() {
var product = new(ProductPrice)
// ...
}
return productPrices
}
Now do the same with getContractProductPricesFromDatabase and finally update the argument types to your processProductPricesFromDatabase function:
func processProductPricesFromDatabase(productPrices []*ProductPrice, contractProductPrices []*ContractProductPrice) []*ProductPrice {
// Loop over contact prices and update relevant product prices
for _, contractPrice := range contractProductPrices {
for _, product := range productPrices {
if contractPrice.AppliesTo == product.Sku {
product.UnitPrice = contractPrice.PricingAdjustmentValue
}
}
}
return productPrices
}
As an alternative, if you want to keep using non-pointer types, you can directly modify the values referenced by the slice by indexing into it.
func processProductPricesFromDatabase(productPrices []ProductPrice, contractProductPrices []ContractProductPrice) []ProductPrice {
// Loop over contact prices and update relevant product prices
for _, contractPrice := range contractProductPrices {
for i, _ := range productPrices {
if contractPrice.AppliesTo == productPrices[i].Sku {
productPrices[i].UnitPrice = contractPrice.PricingAdjustmentValue
}
}
}
return productPrices
}
I'm trying to get iOS devices to discover each other with Bonjour and then connect with InputStream and OutputStream.
The devices can connect to each other, but sending bytes from one device's OutputStream will not trigger the "hasBytesAvailable" event on the other device.
Because I want devices to connect with multiple other devices, I've wrapped each connection in an "ASPeer" object, which I can put in an array to keep track of all my connections.
class ASPeer: NSObject {
let service: NetService
var inputStream: InputStream?
var outputStream: OutputStream?
init(_ service: NetService) {
self.service = service
}
func openStreams() {
guard let inputStream = inputStream, let outputStream = outputStream else {
fatalError("openStreams: failed to get streams!")
}
inputStream.delegate = self
inputStream.schedule(in: .current, forMode: .defaultRunLoopMode)
inputStream.open()
outputStream.delegate = self
outputStream.schedule(in: .current, forMode: .defaultRunLoopMode)
outputStream.open()
}
func closeStreams() {
guard let inputStream = inputStream, let outputStream = outputStream else {
fatalError("closeStreams: failed to get streams!")
}
inputStream.remove(from: .current, forMode: .defaultRunLoopMode)
inputStream.close()
inputStream.delegate = nil
outputStream.remove(from: .current, forMode: .defaultRunLoopMode)
outputStream.close()
outputStream.delegate = nil
}
}
extension ASPeer: StreamDelegate {
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch aStream {
case inputStream!:
switch eventCode {
case .openCompleted:
print("inputOpenCompleted:")
case .hasBytesAvailable:
print("inputHasBytesAvailable:")
var readData = [UInt8](Data(capacity: 4096))
let bytesRead = inputStream!.read(&readData, maxLength: 4096)
if bytesRead > 0 {
print(String(bytes: readData, encoding: .ascii)!)
}
case .errorOccurred:
print("inputErrorOccurred")
case .endEncountered:
print("inputEndEncountered")
default:
break
}
case outputStream!:
switch eventCode {
case .openCompleted:
print("outputOpenCompleted:")
case .hasSpaceAvailable:
print("outputHasSpaceAvailable:")
case .errorOccurred:
print("outputErrorOccurred")
case .endEncountered:
print("outputEndEncountered")
default:
break
}
default:
print("got unknown stream!")
}
}
}
I've added print statements to every single "handle" event for my input and output streams. Here are the output logs when I run the app and try to get the devices to talk to each other:
Device 1
inputOpenCompleted:
outputOpenCompleted:
outputHasSpaceAvailable:
Device 2
inputOpenCompleted:
outputOpenCompleted:
outputHasSpaceAvailable:
When I try to send a message from Device 1 to Device 2, I'm expecting Device 2 to print out "inputHasBytesAvailable". However, I just get extra lines of "outputHasSpaceAvailable" from Device 1:
Device 1
inputOpenCompleted:
outputOpenCompleted:
outputHasSpaceAvailable:
outputHasSpaceAvailable: <--
outputHasSpaceAvailable: <--
Device 2
inputOpenCompleted:
outputOpenCompleted:
outputHasSpaceAvailable:
<-- I'm expecting "inputHasBytesAvailable" here!
What could the issue be? I've double checked my run loops and made sure they are correct. Also, there seems to be a bug with "getInputStream" and I made sure to call "getInputStream" on the main queue to avoid that problem. Is there something else I'm missing?
In addition, I also have a BonjourManager object that manages every one of these "ASPeer" connections. The BonjourManager is what actually creates the connections and sends writes to the OutputStreams.
class ASBonjourManager: NetServiceDelegate {
var peers = [ASPeer]()
// ... more code here but omitted
func netService(_ sender: NetService, didAcceptConnectionWith inputStream: InputStream, outputStream: OutputStream) {
if sender == advertiser {
return
}
if let peer = peers.first(where: { $0.service == sender }) {
OperationQueue.main.addOperation {
// Due to a bug <rdar://problem/15626440>, this method is called on some unspecified
// queue rather than the queue associated with the net service (which in this case
// is the main queue). Work around this by bouncing to the main queue.
assert((peer.inputStream == nil) == (peer.outputStream == nil))
if let _ = peer.inputStream, let _ = peer.outputStream {
inputStream.open()
inputStream.close()
outputStream.open()
outputStream.close()
} else {
peer.inputStream = inputStream
peer.outputStream = outputStream
peer.openStreams()
}
}
} else {
OperationQueue.main.addOperation {
let newPeer = ASPeer(sender)
sender.delegate = self
newPeer.inputStream = inputStream
newPeer.outputStream = outputStream
newPeer.openStreams()
self.peers.append(newPeer)
}
}
}
func connectTo(service: NetService) {
var inStream: InputStream?
var outStream: OutputStream?
let peer = peers.first(where: { $0.service.isEqual(service) })!
//assert(peer.inputStream == nil && peer.outputStream == nil)
if peer.inputStream != nil && peer.outputStream != nil {
return
}
if service.getInputStream(&inStream, outputStream: &outStream) {
peer.inputStream = inStream
peer.outputStream = outStream
peer.openStreams()
} else {
print("getInputStream failed!")
}
}
func sendMessage(_ service: NetService) {
let peer = peers.first(where: { $0.service.isEqual(service) })!
if peer.outputStream!.hasSpaceAvailable {
let message = Array("hello world".utf8)
peer.outputStream!.write(message, maxLength: message.count)
}
}
}
I'm learning Go currently and I made this simple and crude inventory program just to tinker with structs and methods to understand how they work. In the driver file I try to call a method from and item type from the items map of the Cashier type. My method have pointer reciever to use the structs directly instead of making copies. When I run the program I get this error .\driver.go:11: cannot call pointer method on f[0]
.\driver.go:11: cannot take the address of f[0]
Inventory.go:
package inventory
type item struct{
itemName string
amount int
}
type Cashier struct{
items map[int]item
cash int
}
func (c *Cashier) Buy(itemNum int){
item, pass := c.items[itemNum]
if pass{
if item.amount == 1{
delete(c.items, itemNum)
} else{
item.amount--
c.items[itemNum] = item
}
c.cash++
}
}
func (c *Cashier) AddItem(name string, amount int){
if c.items == nil{
c.items = make(map[int]item)
}
temp := item{name, amount}
index := len(c.items)
c.items[index] = temp
}
func (c *Cashier) GetItems() map[int]item{
return c.items;
}
func (i *item) GetName() string{
return i.itemName
}
func (i *item) GetAmount() int{
return i.amount
}
Driver.go:
package main
import "fmt"
import "inventory"
func main() {
x := inventory.Cashier{}
x.AddItem("item1", 13)
f := x.GetItems()
fmt.Println(f[0].GetAmount())
}
The part of the code that really pertains to my problem is the GetAmount function in inventory.go and print statement in the driver.go
A map entry cannot be addressed (as its address might change during map growth/shrink), so you cannot call pointer receiver methods on them.
Detail here: https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/4_pabWnsMp0
As Volker said in his answer - you can't get address of an item in the map. What you should do - is to store pointers to items in your map, instead of storing item values:
package main
import "fmt"
type item struct {
itemName string
amount int
}
type Cashier struct {
items map[int]*item
cash int
}
func (c *Cashier) Buy(itemNum int) {
item, pass := c.items[itemNum]
if pass {
if item.amount == 1 {
delete(c.items, itemNum)
} else {
item.amount--
}
c.cash++
}
}
func (c *Cashier) AddItem(name string, amount int) {
if c.items == nil {
c.items = make(map[int]*item)
}
temp := &item{name, amount}
index := len(c.items)
c.items[index] = temp
}
func (c *Cashier) GetItems() map[int]*item {
return c.items
}
func (i *item) GetName() string {
return i.itemName
}
func (i *item) GetAmount() int {
return i.amount
}
func main() {
x := Cashier{}
x.AddItem("item1", 13)
f := x.GetItems()
fmt.Println(f[0].GetAmount()) // 13
x.Buy(0)
f = x.GetItems()
fmt.Println(f[0].GetAmount()) // 12
}
http://play.golang.org/p/HkIg668fjN
While the other answers are useful, I think in this case it is best just to make non-mutating functions not take a pointer:
func (i item) GetName() string{
return i.itemName
}
func (i item) GetAmount() int{
return i.amount
}