Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server-side State Management #61

Closed
bartbutenaers opened this issue Jul 3, 2023 · 38 comments
Closed

Server-side State Management #61

bartbutenaers opened this issue Jul 3, 2023 · 38 comments
Labels
feature-request New feature or request that needs to be turned into Epic/Story details size:L - 5 Sizing estimation point

Comments

@bartbutenaers
Copy link
Contributor

Hi everybody,

Really nice to hear that Flowforge is supporting the developments of a new dashboard. Thanks for that!!

Developing ui nodes for the old dashboard was rather time consuming, partly due to the msg replay mechanism. Because only the last message is being replayed, but the state of a ui node is most of the time constructed incrementally by a sequence of multiple input messages. Which required a lot of time to get always the correct state on the client side. And for every ui node, you had to solve similar issues...

Thorsten made an entirely different implementation with Flexdash. The state is stored on the server side, which can be manipulated with an API. All delta changes will be send to all existing clients. So the entire state on the client side is always up to date, and clients can update their ui when a delta arrives. And when a new client connects, it automatically gets the entire state. Which imho is much easier to work with, if well designed of course.

So hopefully some ideas from e.g. Flexdash are being taken into consideration...

Good luck with this nice project!!!

Bart

@joepavitt
Copy link
Collaborator

Thanks Bart, this is certainly something we're trying to take into account. Architecturally, I've already made accounts for this, although not fully implemented yet. Thanks for the feedback.

@joepavitt joepavitt moved this to 💡Should Have in Dashboard Backlog Jul 26, 2023
@joepavitt joepavitt added size:L - 5 Sizing estimation point feature-request New feature or request that needs to be turned into Epic/Story details labels Jul 26, 2023
@joepavitt joepavitt changed the title Msg replay mechanism Server-side State Management Jul 26, 2023
@MarianRaphael MarianRaphael moved this from 💡Should Have to In Progress in Dashboard Backlog Aug 3, 2023
@joepavitt
Copy link
Collaborator

Server-side state now in place from all widgets, apart from Chart, which is different due to requiring more than just the latest msg.payload. I'll open up a new issue for the chart specifically, and close this wider issue out.

@bartbutenaers
Copy link
Contributor Author

Hi @joepavitt,
Thanks!!!
To be honest I didn't expect this to be implemented so fast...
Is there any kind of API available to update the server-side state? And can this also can be used in custom ui nodes?

@joepavitt
Copy link
Collaborator

joepavitt commented Aug 11, 2023

No formal API, although you could connect to the SocketIO connection and jump on the existing events we use: https://flowforge.github.io/flowforge-nr-dashboard/contributing/guides/events.html#widget-change

As a first pass, I constrained the focus such that if you refresh the browser, then browser-state is restored (as it's also stored server-side)

@joepavitt
Copy link
Collaborator

can this also can be used in custom ui nodes?

For custom UI nodes, currently we just expose a send event (docs) that would be used to pass on state from the custom node.

There is however a plan to add extensions for ui-template which would enable customisation of the script and style parts of the generated component (issue).

We could also look at adding change() function to the standard set of functions exposed in ui-template to mimic that state change. You'll see in this diagram that there are slightly different workflows for our on-change and on-action (which send() currently triggers).

@bartbutenaers
Copy link
Contributor Author

@joepavitt,
Thanks for the feedback! However I am not fully understanding how it works...

Currently my ui-svg node has a number of different input messages that can be injected into the node, and as a result the client-side DOM of all connected clients are updated.

When you want to do this in Flexdash, it works completely different:

  1. The message is injected
  2. The server-side state of the svg-ui node is updated by calling functions from the server-side API.
  3. Flexdash sends the delta to the connected clients.
  4. The clients can update their DOM based on the delta.

Currently the server-side API (from 2) was rather limited, but convenience methods could be added later on (e.g. to delete an item from an array at index i, ...).

I assume that there are functions that developers can call in dashboard 2.0 to update their server-side state?

@joepavitt
Copy link
Collaborator

Thanks for the clarification.

Currently we aren't able to support additional custom widgets at the moment, but it is in the pipeline.

When we do this work, then we would also flesh out easier function calls to make integration smoother than it currently is.

@joepavitt
Copy link
Collaborator

I had interpetred your use of the "custom nodes", as using ui-template rather than standalone customised widgets

@joepavitt
Copy link
Collaborator

We are also open to source integrations if you would like to include ui-svg as a core part of Dashboard 2.0

@bartbutenaers
Copy link
Contributor Author

@joepavitt,
Sorry for the delay! I had my yearly-near-computerless-week...

  • Ok I completely understand that you do not support custom UI nodes yet. No need to rush anything! I have a severe lack of free time already for quite some months, so I will not be complaining that I can't add custom nodes yet...

  • Nice to know that you are open to include the ui-svg as a standard node! Since the ui-svg node needs to be build again from scratch, it is better if there will be some code/feature review. Just to avoid that it becomes again a little monster.

BTW your collegua @Steve-Mcl was my partner in crime at the time being to develop the ui-svg node ;-)

So summarized this Github issue is about a server-side state that can be managed via an API (like in Flexdash), and the API functions take care of sending the delta to the clients (instead of just replaying the last msg):

image

Note that I don't know anything about Vue.js, so my picture might perhaps be not entirely correct...
Note that I also didn't have time yet to think about which functions could be useful for such an API...

So - unless you don't like the idea of such an API - I would appreciate if you could reopen this issue, so I can keep track of the status in the future. Thanks for your understanding!

@bartbutenaers
Copy link
Contributor Author

Hi @joepavitt,
I have spend my entire evening on troubleshooting an svg related issue with the old dashboard. Due to the old last-msg-replay mechanism, I was totally confused and searching in the wrong direction. I will be very pleased if I get rid of that mechanism in the future...

@joepavitt
Copy link
Collaborator

You may have to explain it a little too me.

We have two separate events:

onLoad - runs on a connection to the browser for a widget

onInput - runs when a message is received.

Up to each widget how to handle these

@bartbutenaers
Copy link
Contributor Author

@joepavitt
Sorry for the delay! I have not been clear...
I meant that I am looking forward to work with D2, because I am sure it will solve a lot of the troubles we had with D1 during development. So no question, simply a compliment ;-)

@bartbutenaers
Copy link
Contributor Author

bartbutenaers commented Jun 13, 2024

Continuing another discussion here, since it is related to the above proposal. It was a confusion I had about the message queue in the notification node, but to generalize the discussion a bit I will use e.g. the ui-led node.

Current setup - msg queue

image

  1. First msg1 is injected to turn the LED on
  2. Then msg2 is injected to set the LED color to red
  3. Next msg3 is injected to turn the LED off
  4. Afterwards msg4 is injected to set the LED color to green

These messages are stored in a queue, and the last message from that queue is being binded to the Vue template.

That works fine but has some disadvantages:

  • Every node developer is responsible for writing boiler plate code to store new input messages in the queue or not. I have to be honest that - after a long day at work - it is not always easy to understand how the current state setup works.
  • After a refresh the last message is replayed, but that contains only a part of the full LED state. In this example, it contains only the state OFF but not the color.

We had a lot of similar troubles at the time being in the old dashboard. Especially for the ui-svg node users it was quite a hell, because an SVG drawing contains LOT's of state stuff: e.g. a floorplan with blinking sensors, and so on... Users created all kinds of workarounds in their flows (to solve the partial state supplied by the last replayed msg), but there was unfortunately no decent solution. For example they queued the last N messages and replayed these N messages after a refresh or when a new client connects. But how do you determine the value of N:

  • If N is too small you might have not enough delta's in memory to be able to restore the original full state again.
  • If N is too large, you end up with bad performance. And you will be doing a lot of useless processing. E.g. set a LED in the drawing to red -> green -> red -> green -> red -> green -> red -> green -> ....

Flexdash setup - server side state

Thorsten had found a clever (and yet rather simple) solution for this problem in Flexdash:

image

So we have the same sequence of messages as in the first screenshot. But on the server side, these messages will be used to update the server side state (which contains all the LED properties).

  • As soon as the server side state is being updated, the state delta is being send automatically to all the connected clients. Those clients update their client side state, to which the Vue template is binded. Which means the LED is updated.
  • When a new client connects, it will fetch the full server side state and store it as client side state.

The state sync between server and client is the responsibility of the dashboard, not the individual ui nodes.

Imho such a setup is much easier to understand for ui node developers, and dashboard contributors. But it becomes now harder to refactor the current queue mechanism, because everything (core ui nodes, third party ui nodes, ....) heavily depend on the current mechanism. That is the reason why I started this discussion at the initial development phase of dashboard D2...

Thanks for reading :-)

@colinl
Copy link
Contributor

colinl commented Jun 14, 2024

I am confused by your description, as the method I have used in the gauge-classic node is more like the flexdash approach than the one you describe. I have a similar problem, in that for a multi-needle gauge, the messages for the needle values come in independently, identified by topic. Also there is other dynamic state data that arrives via other messages. In the js file I join the data from individual messages so that the server always has a full set of state data. The needle values are in an array needles[] for example. I then merge that data into the received message, save it in the state store and send it to the clients. The client uses that state data, not any other data that may be in the message. When a new client connects, or an existing one is refreshed, the latest message is sent, but that contains the full state information so that is fine. It is very simple and appears to work well.

@bartbutenaers
Copy link
Contributor Author

Hi @colinl,
Thanks for sharing your experience!!
But now you are also confusing me ;-)

  • About core ui nodes. They e.g. store the msg when it arrives:
         onMsgInput (msg) {
             this.$store.commit('data/bind', {
                 widgetId: this.id,
                 msg
             })
             ...
         },
    
    The stored last msg is sometimes used directly into a template:
    <v-progress-linear v-model="countdown" :color="messages[id]?.color || (props.colorDefault ? 'primary' : color)" style="display: block; width: 100%" />
    
    And sometimes the last msg is used via a property:
     computed: {
         ...mapState('data', ['messages']),
         value: function () {
             return this.messages[this.id]?.payload
         },
    
    Or it is being used when the widget is loaded:
         // given the last received msg into this node, load the state
         onLoad (msg) {
             // update vuex store to reflect server-state
             this.$store.commit('data/bind', {
                 widgetId: this.id,
                 msg
             })
             this.select(this.messages[this.id]?.payload)
         },
    
    And so on...
  • About your gauge-classic node. I didn't have time to read through your code to see how it works, so forgive me if I create wrong conclusions! When a message arrives in your processMsg, it 'looks' to me that you expect ALL the information to be available in every message, or not? Also you also use the last message to setup everything correctly:
         this.$socket.on('widget-load:' + this.id, (msg) => {
             // load the latest message from the Node-RED datastore when this widget is loaded
             // storing it in our vuex store so that we have it saved as we navigate around
             //console.log(`On widget-load ${JSON.stringify(msg)}`)
             this.processMsg(msg)     // pick up message values
         })
    
    Or am I wrong: that you can somehow inject a message with only part of the information, and that it still works fine after a refresh in some way?

Anyway perhaps it is all clear to everybody else. But I don't see the trees through the forest anymore after a long day at work. I only see a bunch of messages, each of them containing only a part of the state. And that it is a lot of puzzling to extract the full state from all those messages.

Thorstens model seems to be much more clear to me. And most of the boilerplate code would be inside the dashboard, not inside the individual nodes....

@bartbutenaers
Copy link
Contributor Author

Sorry @colinl,
I was in a hurry and overlooked your explanation of your message joining. Will have a look tonight at how you did it! My lunch break is over.
BTW cool that you found a workaround! But the fact that you need to join multiple partial states to get the full state, tells me that the general mechanism is not optimal...

@colinl
Copy link
Contributor

colinl commented Jun 14, 2024

I think I see a much better paradigm to use. I have been too hung up on the concept of messages coming in from node red and the messages going to the client. In fact I don't think they have to be directly related at all. At the moment, considering just one of the properties that may come via a message, I have in the initialisation section
let formattedValue = null
then in onInput:, when a message is received I have

                // pick up msg.formattedValue if present and is a string
                if (typeof msg.formattedValue === "string") {
                    formattedValue = msg.formattedValue
                }
...
                // include formattedValue if present
                if (formattedValue) {
                    msg.formattedValue = formattedValue
                }

                // store the latest value in our Node-RED datastore
                base.stores.data.save(base, node, msg)
                // send it to any connected nodes in Node-RED
                send(msg)

This should also work, and is much cleaner. In initialisation

  // initialise data store
   base.stores.data.save(base, node, {})
   // or if appropriate
   // base.stores.data.save(base, node, {/* default values */})

and in onInput

  // pick up existing stored data
  let storedData = base.stores.data.get(node.id)
  ...
  // pick up msg.formattedValue if present and is a string
  if (typeof msg.formattedValue === "string") {
          storedData.formattedValue = msg.formattedValue
  }
  ...
  // store the latest values in our Node-RED datastore
  base.stores.data.save(base, node, storedData)
  // send it to attached clients
  send(storedData)

At the moment I can't see any reason that would not work. I will give it a try.

@colinl
Copy link
Contributor

colinl commented Jun 14, 2024

Well it nearly works, apart from what I thing is a bug. Perhaps @joepavitt can comment on this. If, in onInput in the js file I do

  let storedData = {property1: <some value>, property2: <some value>}
  base.stores.data.save(base, node, storedData)
  // send it to attached clients
  send(storedData)

then actually it sends the original incoming message to the clients, not the contents of storedData. If I refresh the page then it does send storedData. I seems that send() is hard coded to send the original message object. Even

  msg = storedData
  send(msg)

still sent the original message. The only way I could get it to work was by copying the data across to the original message

                for (const property in storedData) {
                    msg[property] = storedData[property]
                }
                send(msg)

Should I submit an issue Joe, or am I trying to do something silly?

@bartbutenaers
Copy link
Contributor Author

@colinl,
Sorry for the delay!
THANKS A LOT FOR THESE EXPERIMENTS!!! SO MUCH APPRECIATED!!!
And nice to see that the Node-RED community has now an extra ui node developer (slash dashboard contributor) ;-)

I think I see a much better paradigm to use. I have been too hung up on the concept of messages coming in from node red and the messages going to the client. In fact I don't think they have to be directly related at all

Indeed that is the whole point of Thorsten's mechanism. The node receives input messages, and updates the server-side state. That state is then send to the clients, NOT the message itself! The clients don't know the concept of messages at all. They just receive an update of their state.

You almost implemented Thorsten's idea, with a couple of disadvantages:

  • In your case the ui node developer is responsible for sending the state to the clients. While it would be better if the dashboard would offer an API with some convenience methods to update the state (getProperty, setProperty, removeProperty, ...). That way the whole mechanism can be improved later on, without every node developer having to do the same. We need to avoid that changes in this mechanism have to be implemented by every ui node developer, like we had last week...
  • In your case you always send the full state to the client. Which is good enough for most ui nodes, but not e.g. for my ui-svg node, because that one has a LOT of state. Which would result in a lot of bandwidth usage and a lot of useless refreshing of the entire drawings. It would be better if only delta's would be send, except for new clients connecting.
  • There is still a lot of dashboard framework code available to forward your messages to the client, which is unnecessary and very confusing. Once you have your full updated state on the client, you don't need your input messages in the clients at all...

That way the entire message storage/sending/handling could be ditched. Because if it is decided to stick to that, we know from experience (i.e. old dashboard) that we will end up this way:

  • Lot's of edge cases that need to be handled in the code.
  • Lot's of headache both for the dashboard and the ui node developers.
  • Complex code which is very hard to maintain.
  • Few contributors due to the complexity
  • More and more issues over time related to the message based setup.
  • ...

Hopefully I don't offend anybody, because that is not my intention. But I am very worried where we will end up in the end if we continue using messages to the clients, because I have been there before unfortunately. And it is very understandable that people fall into the message based approach trap, because the message sending and replaying seems VERY logical in a Node-RED context. But it is nothing but trouble for the dashboard.

Personally I would love to see a poc in a branch with e.g. the Notification node, to see if we can get a clean codebase that does the job. I am more than happy to help with this with some help from you and Joe, of course if Joe sees the benefits of it...

Hopefully @joepavitt has some time to follow this discussion...

@colinl
Copy link
Contributor

colinl commented Jun 15, 2024

I agree entirely that the current interface could be much improved, for all the reasons you describe. I would have thought that additional methods could be added to the existing interface to make things easier, without breaking existing nodes. I would be happy to help where I can with that, even if only by being a sounding board and testing stuff.

I haven't looked at all about how to send data back from the client into node red. I am looking at developing such a node, so I will be looking at that soon.

@bartbutenaers
Copy link
Contributor Author

BTW the reason why I would like to do a POC with the Notification node, is because that node requires support for a lot of different use cases:

  • State (title, content, buttons, ...) need to be in sync with the client.
  • It supports control messages (show, hide, ...)
  • It supports timers: I have a similar requirement with animation lengths in my ui svg node
  • It can be displayed on 1 client or all clients

@bartbutenaers
Copy link
Contributor Author

I would have thought that additional methods could be added to the existing interface to make things easier, without breaking existing nodes.

Good question. There are only a few ui nodes yet, so I am pretty sure that we could ask them to do it another way. For template nodes I don't know. Never used those, so not sure if they make use of message queues. Because that would indeed be a pitty.

It supports control messages (show, hide, ...)

Just to explain that a bit more. Currently you send a message to the clients to hide or show the notification. Which caused me some troubles last week, because my control message was stored in the queue. And since it had become that way the last message, my notification title was suddenly gone (because that title was not part of my last message).

Instead of sending such control messages to the clients, you would set a visible property in the server-side state to true or false via the input message. That property would then be synced to the client, so the client can show or hide the notification. Which means the visibility has become part of the full state.

It can be displayed on 1 client or all clients

Ah, that might be a party stopper. Not sure at the moment how to handle that. Because if you have one shared state on the server, there is no difference for one specific client. Suppose you want to show the notification on a single client, then the visible property on the shared server-state should stay true, while that property should become false for that single client. I don't see a reusable general solution for that use case yet. So any ideas are welcome! Because I need to solve it also for my ui-svg node later on...

@colinl
Copy link
Contributor

colinl commented Jun 15, 2024

Is the visible property per client the same problem as there is at the moment with data going to an individual client or all clients based on msg._client? I don't know how that is handled.

@bartbutenaers
Copy link
Contributor Author

bartbutenaers commented Jun 15, 2024

@colinl,

I don't have the time anymore to read all the issues, so now talking from my ancient experience...

When a message is injected into the ui node:

  • If it has no msg._client property, then the message content is applicable for all connected clients.
  • If it has an msg._client property, then the message content is applicable only to the specified client.

So it looks like this boils down to something like this:

image

  1. A message arrives in the ui node backend, so the API is called to update the state. The ui developer does not specify which state needs to be updated (client or server or both), because that is up to the dashboard framework.
  2. When the message has a msg._client property, the API will only send the state changes to the specified frontend. But the server-side state is not updated (because it contains information common to all clients).
  3. When the message has no msg._client property, the API will first update the server-side state. Next the API will send the state changes to all the connected clients.
  4. The client-side state changes will be reflected in the client widget via Vue bindings.
  5. When a new client connects afterwards, it will get the up to date server-side state.

I 'think' that such a setup is pretty understandable for users and ui developers.

Does this make sense, or have I overlooked something?

@colinl
Copy link
Contributor

colinl commented Jun 16, 2024

Seems mostly reasonable to me, though I am not familiar with the way msg._client works at the moment.

The ui developer does not specify which state needs to be updated (client or server or both), because that is up to the dashboard framework.

How would the framework know which properties were of interest? Also the data in the message might not map directly to properties. For example, in my gauge node, needle values are passed in with the value in payload and the needle identifier in the topic. That needs to be processed to update the needles array in the data store. I suppose I could make it so that the user passes in a more complex data structure but that might not be so good for the user.

The client-side state changes will be reflected in the client widget via Vue bindings.

Might the client side need to know what properties were updated? There may be some pre-processing that needs to be done, or do you think that should be entirely handled by the vue binding. At the moment in the gauge node I do some pre-processing, but perhaps I can avoid that.

I have realised that I think I can improve the technique I have used with the existing framework to avoid sending the whole state object every time. I need to code it to see if there is a flaw in the idea though.

@colinl
Copy link
Contributor

colinl commented Jun 16, 2024

It does work, and seems obvious now, as most things do once you see how. The key is that the full data store is sent to the clients on refresh. So in the client that can be processed and saved locally. When a new message is received in the .js file it will generally only contain a subset of properties. Those can be used to update the data store (subject to msg._client if necessary) and then passed on with just that subset to the clients. The clients can then pick up any properties in the message and update accordingly.

So now in the .js file initiallisation section I have

  // initialise data store
  base.stores.data.save(base, node, {/* default values */})

And in onInput, showing the code for just one property

  // pick up existing stored data
  let storedData = base.stores.data.get(node.id)
  ...
  // pick up msg.formattedValue if present and is a string
  if (typeof msg.formattedValue === "string") {
          storedData.formattedValue = msg.formattedValue
  }
  ...
  // store the latest values in our Node-RED datastore
  base.stores.data.save(base, node, storedData)
  // send the message containing just modified properties to attached clients
  send(msg)

In fact it is only the last line that has changed, so that it sends the message on the clients, not the full data store. I didn't have to change the client code at all as it already only picked up properties present in the message it was given (which is the full set in the case of a refresh and a subset in the case of a later message). The same code processes the incoming message whether it is the initial full set or just a subset.

I think that is effectively what you have in your flowchart.

@bartbutenaers
Copy link
Contributor Author

How would the framework know which properties were of interest? Also the data in the message might not map directly to properties.

Absolutely correct. I should have added that on my diagram, but now indeed it looks like the input message is being parsed automatically, which is not the case. Consider it done...

There may be some pre-processing that needs to be done, or do you think that should be entirely handled by the vue binding.

Again correct. I had only added the bindings on my diagram, but indeed you should be able to handle client side state changes programmatically. I have added that also to the diagram now...

image

The key is that the full data store is sent to the clients on refresh

Ah really. I wasn't aware that it already worked that way. Because I have been seeing last msg replaying. So I had the impression that only that last msg was available, not the full state...

// store the latest values in our Node-RED datastore
base.stores.data.save(base, node, storedData)

So you always store the values in the datastore. Even when the msg has a _client property (i.e. that the data needs to be send to the specified client only)?

I still believe it is useful that the framework should do all of this automatically behind an API, so ui node developers just can do their thing (without having to learn how everything works behind the scenes).

Thorsten also had some other cool ideas for this. Suppose a notification needs to be visible for 60 seconds.

  • So you could do API.setProperty('displayTime', 60). When a new client connects after 20 seconds, it will get the updated state (containing 60) and as a result show the notification also for 60 seconds.
  • However in some use cases you want the notification to disappear on all clients 60 seconds after the trigger has been given. In that case you could call e.g. API.setRelativeTimeProperty('visible', 60). The 60 is send to all existing clients, but when a new client connects after 20 seconds, the returned full state would contain 40 seconds (= 60 - 20). So the dashboard calculates in this case automatically the remaining time.

An API allows all ui nodes to do it the same way...

@colinl
Copy link
Contributor

colinl commented Jun 16, 2024

So you always store the values in the datastore. Even when the msg has a _client property (i.e. that the data needs to be send to the specified client only)?

I haven't provided any support for the _client property yet. I don't fully understand how it should work,so I need to look at the docs and try some experiments. It is obviously vital that the notification node does support it as that is a fundamental part of its operation. For something like a Gauge node, unless there is full multi-tenancy, so different users can show different data, I am not convinced that it is of much use. One particular point that concerns me, when used with something like a gauge or even a text node, for example. What will happen if the user refreshes the page. I don't see how the previous state is going to be restored.

The key is that the full data store is sent to the clients on refresh

Ah really. I wasn't aware that it already worked that way. Because I have been seeing last msg replaying. So I had the impression that only that last msg was available, not the full state...

The ui-example node has

            onInput: function (msg, send, done) {
                // store the latest value in our Node-RED datastore
                base.stores.data.save(base, node, msg)
                // send it to any connected nodes in Node-RED
                send(msg)
            },

Which saves the latest msg in the store. That is why the latest msg is sent on connection, because that is what is in the data store.

I still believe it is useful that the framework should do all of this automatically behind an API, so ui node developers just can do their thing (without having to learn how everything works behind the scenes).

I agree that would be useful. It could be pretty thin, I think. In your flow chart you have
image

API.setProperty('visible', msg.visible)
would map to something like.
dataStore.visible = msg.visible
and the framework could add the stuff to pick up the data store at the start and save it at the end.

In fact one could imagine it being data driven where the user provides a list of properties of interest and the framework does the rest. With hooks to allow manual processing where necessary.

I haven't looked at all at how to handle sending messages and updating server state from the client yet. That is next on the list.

On a separate issue, in the example app I see, in the vue file


        this.$socket.on('msg-input:' + this.id, (msg) => {
            // store the latest message in our client-side vuex store when we receive a new message
            this.$store.commit('data/bind', {
                widgetId: this.id,
                msg
            })
        })

But I have no idea what that store is for. I have just used vars defined in the data() section for storing client side state.

@joepavitt
Copy link
Collaborator

Appreciate this is a long thread, and I need to catch up properly, but quick note to say that Dashboard internals handle everything re: _client.

Third party nodes don't have to do anything themselves

@joepavitt
Copy link
Collaborator

But I have no idea what that store is for

Client side store means we have a centralised place all the data is stored across the app, without a need to keep checking in on the state from the server-side.

data () is scoped to a single component/widget only

@joepavitt
Copy link
Collaborator

https://dashboard.flowfuse.com/contributing/guides/state-management.html

Is the best docs I have for detailing the different stores, the server-side API, and it's purpose

@colinl
Copy link
Contributor

colinl commented Jun 17, 2024

Dashboard internals handle everything re: _client.

Third party nodes don't have to do anything themselves

I don't see how that can be the case. What will I see in my onInput function if msg._client is present?

@colinl
Copy link
Contributor

colinl commented Jun 17, 2024

Client side store means we have a centralised place all the data is stored across the app, without a need to keep checking in on the state from the server-side.

data () is scoped to a single component/widget only

OK, but in my widget, what use is it to me?

@joepavitt
Copy link
Collaborator

I don't see how that can be the case. What will I see in my onInput function if msg._client is present?

You have access to the whole msg._client object if you want to use it for any reason, but you don't need to worry about restricting socket connections, etc. You'll only be running your function if Dashboard has deemed it a valid connection

@joepavitt
Copy link
Collaborator

OK, but in my widget, what use is it to me?

It's a best practice to store your incoming msg values there, and then your component can reference the store directly, rather than every widget managing the state of their own msg variables within the scope of their own component.

By having it centralised, it means we have a single source of truth on the client-side, not having the data distributed to each node.

@colinl
Copy link
Contributor

colinl commented Jun 17, 2024

Dashboard internals handle everything re: _client.

Ah, having done some experiments I see that the msg._client test is done when my server side code runs send(msg). Only sending it to appropriate clients. I have some issues with how that is working, but I will start a forum thread to ask about that.

@colinl
Copy link
Contributor

colinl commented Jun 17, 2024

By having it centralised, it means we have a single source of truth on the client-side, not having the data distributed to each node.

I don't understand what that means in practice but again will start a forum thread. It is separate to the discussions here I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request New feature or request that needs to be turned into Epic/Story details size:L - 5 Sizing estimation point
Projects
Status: Done
Development

No branches or pull requests

3 participants