Holochain Forum

Basic send/receive emit_signal Zome setup

Hey @freesig
thanks for coming out today!
as per our quick convo I’m trying to setup my environment to use the emit_signal method.
seems to be a much neater way to set a value then have it broadcast to all my receiving agents and solves my issues of parsing the entire chain from the receiver nodes.

Phil showed me this approach and gave me some instruction but I’m struggling to implement the basic zome setup.

Here’s the gist for the Zome setup but this currently doesn’t compile.

Could you have a quick look and let me know if you can see what I’ve messed up here.
:slight_smile: cheers - sim

You just had a few little errors.
This one compiles :slight_smile:

thanks Tom, but still having some dramas on this.
In particular I’m having a type mismatch error.
Have put some details in the Gist.
Thanks Tom (and you too @pauldaoust and sorry for the newb questions and being annoying with my rookie rust mistakes!)

cheers. sim

Left a comment on the gist

success!!! thanks so much Tom:pray::+1:

Heya Tom.
This worked well above but having some challenges on working out the next bit here. Which is from my rust program that connects to this zome how to configure the commit entry and receive functions.

My current function connects to the websocket ok but I get the following error:

Set to DHT Worked: Text(
    "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Public token not found: No public CapTokenGrant entry type in chain\"},\"id\":\"signal\"}",
)

Here’s my current program function:

After solving that I’ll need to setup the receive end (i.e. setup a variable that will listen for the emit). I will do some tinkering to work out but wondering if you have a code snip for this already?

Thanks Tom.

You cannot call the receive function directly. You must call send and then the receive function is called when the message is received. Does that make sense?

kind of Tom, but still struggling.

So being that I need to call ‘send’ is this the right function below that I need to add to my zome? Or is it a different one that doesn’t require a dest agent address I should use (i.e. one that just emits to anyone running the zome that is listening)?:

#[receive]
pub fn receive(_address: Address, _message: JsonString) -> String {
    hdk::debug(format!("New message from: {:?}", _address)).ok();
    let success: Result<Signal, _> = JsonString::from_json(&_message).try_into();
    match success {
        Err(err) => format!("error: {}", err),
        Ok(message) => {
            let r = hdk::emit_signal(
                message.signal.as_str(),
                JsonString::from_json(&format!("{{message: {:?}}}", message)),
            );
            json!(r).to_string()
        }
    }
}

#[zome_fn("hc_public")]
fn send(to_agent: Address, message: String) -> ZomeApiResult<String> {
    hdk::send(to_agent, message, 60000.into())
}

And then my Rust program look a bit like this:

I think there’s some miss-understanding here.

Send and Receive

So the point of this is for Alice to send a direct message to Bob.
You can call Alice’s send function like you have above with Bob’s agent address as the to_agent parameter.
Then Bob’s receive function will be called when Bob comes online and receives the message.

So the fn rec_from_dht() doesn’t really make sense as you are trying to call your own agents receive function directly.

Signals

The hdk::emit_signal in the receive function is what will inform your UI.
This is how basic chat emits a signal: https://github.com/holochain/basic-chat/blob/a07898358f6aef7278649191d0835f11ae42c9ff/dna-src/zomes/chat/code/src/lib.rs#L80
and this is how it’s received by the UI: https://github.com/holochain/basic-chat/blob/a07898358f6aef7278649191d0835f11ae42c9ff/ui-src/src/index.js#L234

Rust middleware

I assuming you are trying tom use signals in a Rust middleware application?
I have never actually done this although would be very interested in working out how to do it. I’ll have a dig around. The signals were definitely designed with the UI in mind although I see no reason why they couldn’t be used with a middleware like this.
Let me know if any of my assumptions are wrong here

2 Likes

I think emit will just send a message to your websocket connection the same way a call sends a response. Although the format might be slightly different.

You could just wait on the recv channel and then run a function that handles emit messages.

Thanks Tom.

Send and Receive

Ok so this is not really the setup Im after. Rather I want Alice to send a signal and have it heard by a community of agents who are running the dna.

Signals

This is definitely the feature Im after. Alice signals the group and all the listening agents receive it.
Previously I had Alice sending the signal and then a looping get function where bob would read the dht whether it changed of not.
Had a brief look and these chat zome links are super handy, and I think will help me solve this. Ill have a play with them today and see if I can get them working for my setup.

Rust middleware

Thats right, maybe I’m a sucker for punishment but I’m trying to use Rust for the entire setup at the moment. My basic setup is:

Alices App (rust - provides a signal to the community)

  • provides a basic UI
  • queries the energy market external API every 5 minutes
  • sets a ‘threshold value’ (low/med/high) based on the current price
  • if that threshold is different from the current it updates it in the DHT
  • Sets the value through websocket in Holochain

Bob App (rust - recieves a signal from alice)

  • provides a basic UI (select my device, and appetite for you to switch my device
  • program constantly monitors the DHT for signals from Alice
  • when a signal is heard Bob will implement a loop based on the current value (i.e. if low it will send an api call to my AC to update its setpoint or comfortability range)

In the next iteration we’ll be shifting the UI bits to a mobile app, and I plan on adding some more complexity and logic to the the ‘signal’ messaging but really at the moment I’m just needing ‘alice’ to commit an entry and for that action to be broadcast and heard by bob (and others like bob running the same zome).

as above the links above look really helpful so I’ll have a play today and update my Gist if I can get this going.

thanks again Tom :slight_smile:

Actually a signal is probably not what you’re looking for. It just sends a message across a websocket. Usually locally and only to a signle end point.
I think if you want to send something to many agents you will want to have a list of their addresses and send / receive to each agent.

There’s currently (as far as I know) no way to broadcast to a DHT. You can either commit an entry and get the other agents to poll (keep trying to get it) or gather a list of agent id’s (agent addresses) and direct message (n2n messaging / send receive).
The second is more immediate but you need to know who you’re sending to.

oh ok thanks Tom.
this is basically how my old code operated to have agents polling the dht every 2.5 minutes for the latest value whether it was changed or not.

I was hoping that I’d be able to use this emit and signal feature as a way for an agent to set a value and broadcast it to a community running the same zome in a way that would avoid:

  • having to deal with issues around the size of my chain continuously growing. In my setup my receiving agents only ever really need the current value or at the most the last 1-3 not the entire history). My hope was the signal approach would get around that problem as it is just sending a single value at a point in time to others running the same dna.
  • next I was hoping that this would be a nice way I could use to avoid my signal receiving agents having to do unecessary work polling the DHT when the value had not changed. i.e. Using the signal-emit approach the receiving program would be informed only when the value was set(or signal feature was called) rather than having to continuously check.

bummer!
:slight_smile:

I’m not honestly sure you need to worry about the size of the chain at least at this point in time. You’re talking about a very small amount of data and even micro computers have gigabytes of storage these days. It might take a very very long time to fill them and you can always just upgrade to a new DNA and truncate the data if it’s no longer needed for validation.

Is there any way you could get agents to register there agent IDs when joining? This is pretty much what chat does and then you could just use the send / receive messages to message all registered agents when an update comes through?

No worries thanks Tom. Thinking this through actually both methods of getting the signal will probably be useful for me as there’s two tiers I need to think about for these signals.

  1. I need to send signals to all the devices that are owned and managed by an individual or named ‘energy manager’. This will be a known list of endpoints so tx/rx seems the right thing to do there.

  2. There will also be a signal market place that the energy manager uses to select and subscribe his devices to through a mobile app. Makes sense that this would use the commit entry DHT model.

My thinking is that the workflow is:

  • energy manager through the app searches and subscribes to DHT(s) within the marketplace that are interesting to them.
  • the energy manager (agent) app ‘onsends’ relevant signals using the tx/rx method.

I’ll work through this with our team as not sure if this will compromise the ‘agent sovereignty’ goals (as device decisions are censored by the energy manager app) but will chat.

Long story short I think I’ll need to implement some elements of both options here.
Thanks Tom.

Hi Tom.
Just wondering on the send receive method do you have any examples I could reference of how to access this via a rust program through websocket?

i.e. an example of the function call for each side sender and receiver of the message?

I’m trying to work out what the call needs to look like on either sid.
Here’s what Im sending over the socket at the moment on my send side:

let json = serde_json::json!(
    {"id": "signal",
     "jsonrpc": "2.0",
     "method": "call",
     "params": {"instance_id": "test-instance",
     "zome": "signal_node",
     "function": "send",
     "args": {"_to_agent": "HcSCiGH5EW8O3Cc7if9PC9xNkCXFoepsxVJtPHj83yMghvrfGeqkM6mSuw87kqa",
            "_message": "Message for the receiver",
        }
    }}); 

In my zome I have the following function:

#[zome_fn("hc_public")]
fn send(_to_agent: Address, _message: String) -> ZomeApiResult<String> {
    hdk::send(
        _to_agent,
        _message,
        300000.into(),
    )
}

This setup however returns me an error:

Send to Agent Call Return: Text(
    "{\"jsonrpc\":\"2.0\",\"result\":\"{\\\"Ok\\\":\\\"error: expected value at line 1 column 1\\\"}\",\"id\":\"signal\"}",
)

my websocket is working ok and I can callback get_agent_id functions.
Also I’m unsure about how to catch the response in the receiving agent… as it what call do I need to make to the zome for that to work or is it just some sort of listening function in the websocket?

If there’s any examples you might have I can likely muddle my way through it.

cheers - sim

hmm I’m not sure why that’s not working.
This is how I do my calls from rust https://github.com/freesig/holochain-realworld-example-app/blob/f4aba84c56454eef1972fe947f9099ef9bad6de2/middleware/src/lib/ws/mod.rs#L105
I would try changing the id to 0 and see if that works maybe?

ok been a while since I’ve checked this one. and I was able to get the send working by changing the id to 0. As below:

pub fn send_to_agent(_agent: String, _message: String) {// update so it returns a Result.
        let json = serde_json::json!(
            {"id": "0",
             "jsonrpc": "2.0",
             "method": "call",
             "params": {"instance_id": "test-instance",
             "zome": "signal_node",
             "function": "send",
             "args": {"_to_agent": _agent,
                    "_message": _message,
                }
            }});

The message I get is the following:

Text(
    "{\"jsonrpc\":\"2.0\",\"result\":\"{\\\"Err\\\":{\\\"Internal\\\":\\\"{\\\\\\\"kind\\\\\\\":\\\\\\\"Timeout\\\\\\\",\\\\\\\"file\\\\\\\":\\\\\\\"crates/core/src/nucleus/ribosome/runtime.rs\\\\\\\",\\\\\\\"line\\\\\\\":\\\\\\\"220\\\\\\\"}\\\"}}\",\"id\":\"0\"}",
)

This is better than before and it is just timing out because I need to implement the function for the message Receiver.

Would you have any examples of a rust function I need to write on the receiver side that would catch the message?

Here’s one :slight_smile:
https://github.com/holochain/basic-chat/blob/a07898358f6aef7278649191d0835f11ae42c9ff/dna-src/zomes/chat/code/src/lib.rs#L101

1 Like