When to refresh the binding on a table edited by multiple-clients?

I’ve got a simple configurations table in my Perspective application. It’s used to populate various dropdowns in other views. Multiple users can edit the table. It seems to me …

  1. I don’t want a polling binding to the database as it’s wasteful.
  2. I want to keep the user in the same pager page and on the same selected row. The binding refresh would have to respect that.
  3. I should indicate to the user that the data has been changed and, perhaps, give the opportunity to refresh by button.

How do others manage this? Do I create a memory tag to log the last update time and bind the refresh button’s visibility to that?

Any ideas?

Sounds like a job for message handlers.
When a client saves changes, a message is sent to all other clients informing them a change has taken place. Then you can either refresh the table automatically or prompt the user to manually refresh.

2 Likes

OK. I’ll read up on those. I’ve watched the video so I should be able to figure it out.

Thanks.

I can't get this to work reliably. (I thought I saw it work once between a Chrome and Edge browser on the one PC.)

Here's the sendMessage in my table's onEditCellCommit script:

			msg = system.util.sendMessage(
				project = system.project.getProjectName(),
				messageHandler = 'configUpdate', 
				payload = {'table': 'config'}, 
				scope = "C"
			)

Here's the Edit Message Handler to set my table refresh button's visibility. It's on the button's Message Handlers configUpdate script.

def onMessageReceived(self, payload):
	system.perspective.print('configUpdate message received')
	if payload.has_key('table'):
		if payload['table'] == 'config':    # Corrected after code_skin's comment.
			self.meta.visible = True
			system.perspective.print('Refresh visible')
			

I have the Listen Scopes set to Session + Page + View in an attempt to get it to work. The message handler is not firing on update of the table cell.

Can anyone clarify what settings I require to sendMessage all active clients that the table has been update?

  1. What scope should be specified in the sendMessage?
  2. What scope should be specified in the message handler?

Update: When I print the msg variable I get the following which I suspect is one of our guys using the Vision client version of the application. I don't see any sign of me using the Perspective version.

[type=Client,sessionId=D292FDD2,clientAddress=10.X.X.X,clientHostName=UsersHostname, project=MyProject,messageHandler=configUpdate,filterParams={userRoles=, scope=C, userZones=},sendStatus=SENT]

Many thanks.

This might be an issue.

Try:
if payload['table'] == 'config':

Oops! Thanks. I’ve fixed that but the event isn’t being called. I have system.perspective.print statements before that and they don’t execute.

Can you spot anything with my scoping?

Thanks!

This looks wrong too. try changing this to system.util.getProjectName()
nvm.

Yes, what is scope='C' ?

You need to specify perspective scopes here; session, page, or view

String scope - The scope that the message should be delivered to. Valid values are "session", "page", or "view". If omitted, "page" will be used. [optional]

https://docs.inductiveautomation.com/display/DOC81/system.perspective.sendMessage

They seemed to use shorthand 'S', 'C' or 'G' (gateway) somewhere in the documentation. I've changed to 'session' and the returned value from sendMessage is

[type=Session,sessionId=5a8074b9-acde-4b50-b52f-c17380322f84,
project=MyProject,
messageHandler=configUpdate,
filterParams={userRoles=, scope=session, userZones=},
isRequest=no,
sendStatus=SENT,
type=Session,
sessionId=dbf2304c-01bf-4ffa-b3fd-5aba862d7341,
project=MyProject,
messageHandler=configUpdate,
filterParams={userRoles=, scope=session, userZones=},
isRequest=no,
sendStatus=SENT]

Am I right in thinking that I can send directly from one client to all the others? I don't need to send it to the gateway and have it broadcast it, do I?

Oh. I think you want to use system.perspective.sendMessage and not util.sendMessage. they are different.

2 Likes

You are confusing system.util.sendMessage with system.perspective.sendMessage. In this case, if all editing is done in Perspective, sending to Perspective’s session scope should be the right answer.

2 Likes

Perspective scripts are already running in the gateway.

Doesn't system.perspective.sendMessage only send the the client the code is triggered from.

Send a message to a message handler within the same session. system.perspective.sendMessage.

I want to notify all clients that the table has been updated.

Ah, yes, you do need system.util.sendMessage, but with scope=‘S’. Scope ‘C’ broadcasts to Vision clients.

Thanks, guys. No success yet. When I run the following code the first two messages return the expected values but msg is empty.

	msg = system.perspective.sendMessage(
		project = system.project.getProjectName(),
		messageHandler = 'configUpdate', 
		payload = {'table': 'config'}, 
		scope = "S"
	)
	system.perspective.print(system.project.getProjectName())
	system.perspective.print('sendMessage configUpdate')
	system.perspective.print(msg)

See edited emphasis above.

Thanks, Phil. (I'm blaming you for that one from earlier. :wink:)

Now I'm getting a response to my sendMessage:

[type=Session,sessionId=5a8074b9-acde-4b50-b52f-c17380322f84,
project=MyProject,
messageHandler=configUpdate,
filterParams={userRoles=, scope=S, userZones=},
isRequest=no,
sendStatus=SENT,
type=Session,
sessionId=dbf2304c-01bf-4ffa-b3fd-5aba862d7341,
project=MyProject,
messageHandler=configUpdate,
filterParams={userRoles=, scope=S, userZones=},
isRequest=no,
sendStatus=SENT]

so it looks like it's seeing another client. The clients still aren't triggering. (I just have a system.perspective.print in the onMessageReceived for now. Listen Scopes is set to Session.)

Reading through the documentation again, I'm beginning to think that component message handlers can't see system.util.sendMessage messages but only system.perspective.sendMessage messages. Component message handlers says:

The idea being that some other component will broadcast a message, and if the type of the message matches what the Message Handler is listening for, the Message Handler will execute a script.

That means I am going about this the wrong way and that I need to:

  1. Create a Project Browser | Session Events | Message | configUpdate session message handler to receive a broadcast message.
  2. Have that create a system.perspective.sendMessage and pass the payload to the components in the session.

I can't get the session message handler to detect the incoming message though.

Am I on the right track?

It's a little bit simpler than that.
system.perspective.sendMessage() takes a sessionID parameter that you can use to directly send messages to other sessions.

Try something like this:

def broadcastPerspectiveMessage(messageType, payload):
	"""Broadcast a Perspective message to all sessions in the current project."""
	project = system.util.getProjectName()
	sessions = system.perspective.getSessionInfo(projectFilter=project)	
	scope = 'session'
	for session in sessions:
		try:
			system.perspective.sendMessage(messageType, payload, scope=scope, sessionID=session['id'])
		except:
			# Try/except to avoid 'Unvalid UUID String' errors from open designer sessions.
			pass

Then on your save button:

payload = {
	'sessionID': self.session.props.id
}
shared.util.broadcastPerspectiveMessage("ConfigTableSaved", payload)

And on a message handler on your table (with message type of ConfigTableSaved listening to Session messages):

if payload['sessionID'] == self.session.props.id:
	# Do something on the session that initiated the save.
	pass
else:
	# Do something on all other listening sessions.
	pass
		
# Do something for all sessions.
pass

Thanks for this information. I haven’t found anything like this. All the documentation and examples suggest that I can just send a broadcast using system.util.sendMessage and the others will pick it up. Your approach seems to target them all individually.

Q1. Where does the broadcastPerspectiveMessage code go? Scriptiong | Project Library or on the gateway?
Q2. You seem to be bypassing the Session Event message handler and going directly to the component message handler. Can you do that?