[Feature-2249] Saving Session Information

Is there a preferred way of saving the session information for a user? Usually in the web world, we can either store the session in a cookie or local storage of the machine. I understand that we can also save the information into the database and the write it back to the user upon login. I’m just wondering if there is a preferred way to do this in Ignition.

Hi @tony.petruzzi1, you can store information in the database against the user information. You can pull this from the session props for the current user, or there are some inbuilt scripting functions to query for other user’s info (which function depends on whether you are using Vision or Perspective). Not sure if this is the best way, but it’s the way I do it :slight_smile:

Additionally, if you ever want to store information for a specific device, one way is to use a fixed IP address and store information against this. This is the ‘host’ session prop.

If we wanted to go the host route, how would we apply the information when the session start? I tried putting a default value in the Session OnState event as a test, but it doesn’t get set.

session.custom.tony = ‘Tony!!!’

I have used bindings on the components you need to change. Or you can use a change script on the host prop to change another value.

what I mean is there a way to initialize the session variables on Startup? In the web world, there is usually an OnApplicationStart event or what not, that you can use to initialize session variables. I thought that the Session onStart event was such a thing, but I seem to be wrong

Unfortunately, the Session Event Scripts (in this case Startup) run in the Gateway and are triggered before the session props are evaluated. The session props are populated once the session has started.

Bindings and/or onChange scripts will update when the session prop has been populated, effectively acting like it’s on startup (but you may see a short delay). As far as I am aware there is no way to initialise the session properties before/during session startup.

That is unfortunate. You would think that by version 8 they would have something like that. I’ll see about putting in a feature request.

I may be wrong, and maybe @cmallonee or @PGriffith may be able to advise further :slight_smile:

Just for my understanding, in your use case scenario why would a binding or change script not work? I’m just curious :slight_smile:

So… Session Startup is not the correct place, because no information is really in place yet. Also, Change scripts on the auth properties won’t work because if your project requires authentication then those properties initialize with the user’s information, which does not qualify as a “change event”. If your project does NOT require authentication, then change scripts are a pretty good way to go.

I think the Page Startup Event might be a good place to put this, although you would need to include a session variable which acts as a flag to prevent multiple executions.

Something like:

if not page.session.custom.user_info_set:
    # do your DB stuff here, including setting session props
    page.session.custom.user_info_set = True
2 Likes

Thank you, I will look into doing this :slight_smile:
In all honesty, I’m kind of surprised that this isn’t included already. You would figure that there would be someway to initialize a session.

We have a long-standing feature request for something like this (Login Event) which is where you would really want this logic; after the session has started and after the user properties have been applied to the session. The feature hasn’t had much traction (opened July 2019) so I expect it’ll still be awhile before it’s given consideration and implemented.

Just an update. I actually got it working where it will store session information into the database based on the hostname of the machine. I create a table called SessionStorage with a HostName column as the primary key and a column for each setting I want saved. I used a named query to retrieve the information and a bunch of Libraries to lookup the hostname (using nslookup) and interact with the database. Here is the code for it all… I hope it helps someone else:

Project Library - Devices

def GetHostName(Host):
	"""
	This will do an nslookup and get back the results like below 
	
	Server:  dns1.example.com
	Address:  172.20.5.5
	
	Non-authoritative answer:
	Name:    LAPTOP.example.com
	Address:  10.50.6.6
	
	it will then parse the output plucking the "Name:" line and
	returning the FQDN for the client.
	"""
	import subprocess
	# perform the nslookup
	proc = subprocess.Popen(["nslookup", Host], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
	output = proc.stdout.read()
	# convert the output to a list (array)
	lines = output.split('\n')
	name = ""
	# look for the "Name:" line	
	for line in lines:
		if "Name:" in line:
			name = line
	# return if we don't find it
	if not name:
		return name
	# split the name line into a list and return the last part (FDQN)
	# make sure to strip it
	return name.split(' ')[-1].strip()

Project Library - Sessions

"""
This library is used to retrieve and set Session informatin from the database based on
the device's Hostname or IP address.

Session information are stored in the SessionStorage table

BTW... Yes I know it is bad practice to use a '*' in a query, but
I did this so you don't have to update the Named Query everytime you
need to add a column in the table. Just deal with it ;)
"""

def GetSetting(HostName, Setting):
	"""
	Retrieves the setting. Return a blank string if not found
	"""
	Value = ''
	_HostName = Devices.GetHostName(HostName)
	params = {"HostName": _HostName}
	system.perspective.print(params)
	
	results = system.db.runNamedQuery("Session/SessionInformation", params)
	system.perspective.print("Found: {0}".format(results.getRowCount()))

	if results.getRowCount() == 1:
		Value = results.getValueAt(0, Setting)
		
	return Value
	
	
	
def StoreSetting(HostName, Setting, Value):
	"""
	Performs a UPSERT for a setting. In other words, if there isn't a row for
	the HostName in the table, it will create one.
	
	Returns the number of row affected (which should be 1)
	"""
	_HostName = Devices.GetHostName(HostName)

	system.perspective.print(Value)	
	system.perspective.print(_HostName)

	query = """merge into SessionStorage as Target
	using ( select HostName = ? ) as Source
	on Target.HostName = Source.HostName
	when matched then 
	update set Target.{0} = ?
	when not matched then
	insert (HostName, {0}) values (?, ?);""".format(Setting, Setting)
	system.perspective.print(query)
	
	args = [_HostName, Value, _HostName, Value]
	system.perspective.print(args)
	system.perspective.print("Writing Setting {0} to Database for {1}".format(Setting, _HostName))
	results = system.db.runPrepUpdate(query, args)
	system.perspective.print("Setting Saved to Database for {0}".format(_HostName))
	system.perspective.print(results)
	
	return results

Named Query - Sessions/SessionInformation

SELECT top 1 *
FROM
	SessionStorage 
WHERE
	HostName = :HostName

Component Event - onStartUp Script

self.props.value = Sessions.GetSetting(self.session.props.host, "Monitor_LineNumber")

Component Event - onActionPerformed Script

Sessions.StoreSetting(self.session.props.host, "Monitor_LineNumber", self.props.value)

1 Like

BIG THANKS Tony.Petruzzi. My application is very similar to yours. And after spending two days finding that the session properties and …getHostName functions don’t provide the answers, your GetHostName function was just what I needed.

I REALLY APPRECIATE THE ASSIST!

:hugs: