@ -24,7 +24,7 @@ import logging; logger = logging.getLogger("nsm-data"); logger.info("import")
URL="https://www.laborejo.org/agordejo/nsm-data"
URL="https://www.laborejo.org/agordejo/nsm-data"
HARD_LIMIT = 512 # no single message longer than this
HARD_LIMIT = 512 # no single message longer than this
VERSION= 1.0
VERSION= 1.1
#In case the user tries to run this standalone.
#In case the user tries to run this standalone.
import argparse
import argparse
@ -90,13 +90,15 @@ class DataClient(object):
loggingLevel = "error", #"info" for development or debugging, "error" for production. default is error.
loggingLevel = "error", #"info" for development or debugging, "error" for production. default is error.
)
)
#Add custom callbacks. They all receive _IncomingMessage(data)
#Add custom callbacks. They all receive _IncomingMessage(data)
self.nsmClient.reactions["/agordejo/datastorage/setclientoverridename"] = self.setClientOverrideName
self.nsmClient.reactions["/agordejo/datastorage/setclientoverridename"] = self.setClientOverrideName
self.nsmClient.reactions["/agordejo/datastorage/getclientoverridename"] = self.getClientOverrideName
self.nsmClient.reactions["/agordejo/datastorage/getclientoverridename"] = self.getClientOverrideName
self.nsmClient.reactions["/agordejo/datastorage/getall"] = self.getAll
self.nsmClient.reactions["/agordejo/datastorage/getall"] = self.getAll
self.nsmClient.reactions["/agordejo/datastorage/getdescription"] = self.getDescription
self.nsmClient.reactions["/agordejo/datastorage/getdescription"] = self.getDescription
self.nsmClient.reactions["/agordejo/datastorage/setdescription"] = self.setDescription
self.nsmClient.reactions["/agordejo/datastorage/setdescription"] = self.setDescription
#self.nsmClient.reactions["/agordejo/datastorage/read"] = self.reactRead
self.nsmClient.reactions["/agordejo/datastorage/gettimelinemaximum"] = self.getTimelineMaximum
self.nsmClient.reactions["/agordejo/datastorage/settimelinemaximum"] = self.setTimelineMaximum
#self.nsmClient.reactions["/agordejo/datastorage/read"] = self.reactRead #generic key/value storage
#self.nsmClient.reactions["/agordejo/datastorage/readall"] = self.reactReadAll
#self.nsmClient.reactions["/agordejo/datastorage/readall"] = self.reactReadAll
#self.nsmClient.reactions["/agordejo/datastorage/create"] = self.reactCreate
#self.nsmClient.reactions["/agordejo/datastorage/create"] = self.reactCreate
#self.nsmClient.reactions["/agordejo/datastorage/update"] = self.reactUpdate
#self.nsmClient.reactions["/agordejo/datastorage/update"] = self.reactUpdate
@ -110,8 +112,11 @@ class DataClient(object):
sleep(0.05) #20fps update cycle
sleep(0.05) #20fps update cycle
def getAll(self, msg):
def getAll(self, msg):
"""A complete data dumb, intended to use once after startup.
"""A complete data dump, intended to use once after startup.
Will split into multiple reply messages, if needed"""
Will split into multiple reply messages, if needed.
Our mirror datastructure in nsmservercontrol.py calls that on init.
"""
senderHost, senderPort = msg.params
senderHost, senderPort = msg.params
path = "/agordejo/datastorage/reply/getall"
path = "/agordejo/datastorage/reply/getall"
encoded = json.dumps(self.data)
encoded = json.dumps(self.data)
@ -133,10 +138,10 @@ class DataClient(object):
def setDescription(self, msg):
def setDescription(self, msg):
"""
"""
Answers with descriptionId and index when data was received and saved.
Answers with descriptionId and index when data was received and saved.
The GUI needs to buffer this a bit. Don't send every char as single message.
The GUI needs to buffer this a bit. Don't send every char as single message.
This is for multi-part messages
This is for multi-part messages
Index is 0 based,
Index is 0 based,
chunk is part of a simple string, not json.
chunk is part of a simple string, not json.
@ -148,8 +153,8 @@ class DataClient(object):
if not self._descriptionId == descriptionId:
if not self._descriptionId == descriptionId:
self._descriptionId = descriptionId
self._descriptionId = descriptionId
self._descriptionStringArray.clear()
self._descriptionStringArray.clear()
self._descriptionStringArray[index] = chunk
self._descriptionStringArray[index] = chunk
buildString = "".join([v for k,v in sorted(self._descriptionStringArray.items())])
buildString = "".join([v for k,v in sorted(self._descriptionStringArray.items())])
self.data["description"] = buildString
self.data["description"] = buildString
self.nsmClient.announceSaveStatus(False)
self.nsmClient.announceSaveStatus(False)
@ -158,7 +163,7 @@ class DataClient(object):
for the GUI/host to use the original name!"""
for the GUI/host to use the original name!"""
clientId, senderHost, senderPort = msg.params
clientId, senderHost, senderPort = msg.params
path = "/agordejo/datastorage/reply/getclient"
path = "/agordejo/datastorage/reply/getclient"
if clientId in self.data["clientOverrideNames"]:
if clientId in self.data["clientOverrideNames"]:
name = self.data["clientOverrideNames"][clientId]
name = self.data["clientOverrideNames"][clientId]
else:
else:
logger.info(f"We were instructed to read client {clientId}, but it does not exist")
logger.info(f"We were instructed to read client {clientId}, but it does not exist")
@ -167,7 +172,7 @@ class DataClient(object):
self.nsmClient.send(path, listOfParameters, host=senderHost, port=senderPort)
self.nsmClient.send(path, listOfParameters, host=senderHost, port=senderPort)
def setClientOverrideName(self, msg):
def setClientOverrideName(self, msg):
"""We accept empty string as a name to remove the name override.
"""We accept empty string as a name to remove the name override.
"""
"""
clientId, jsonValue = msg.params
clientId, jsonValue = msg.params
name = json.loads(jsonValue)[:HARD_LIMIT]
name = json.loads(jsonValue)[:HARD_LIMIT]
@ -179,6 +184,32 @@ class DataClient(object):
del self.data["clientOverrideNames"][clientId]
del self.data["clientOverrideNames"][clientId]
self.nsmClient.announceSaveStatus(False)
self.nsmClient.announceSaveStatus(False)
def getTimelineMaximum(self, msg):
"""
In minutes
If the GUI supports global jack transport controls this can be used to remember
the users setting for the maximum timeline duration. JACKs own data is without an upper
bound."""
senderHost, senderPort = msg.params
path = "/agordejo/datastorage/reply/gettimelinemaximum"
if "timelineMaximumDuration" in self.data:
numericValue = self.data["timelineMaximumDuration"]
else:
logger.info(f"We were instructed to read the timeline maximum duration, but it does not exist yet")
numericValue = 5# minutes.
listOfParameters = [json.dumps(numericValue)]
self.nsmClient.send(path, listOfParameters, host=senderHost, port=senderPort)
def setTimelineMaximum(self, msg):
"""In minutes"""
jsonValue = msg.params[0] #list of 1
numericValue = json.loads(jsonValue)
if numericValue <= 1:
numericValue = 1
self.data["timelineMaximumDuration"] = numericValue
self.nsmClient.announceSaveStatus(False)
#Generic Functions. Not in use and not ready.
#Generic Functions. Not in use and not ready.
#Callback Reactions to OSC. They all receive _IncomingMessage(data)
#Callback Reactions to OSC. They all receive _IncomingMessage(data)
def reactReadAll(self, msg):
def reactReadAll(self, msg):
@ -249,10 +280,15 @@ class DataClient(object):
self.data = None
self.data = None
logger.error("Will not load or save because: " + e.__repr__())
logger.error("Will not load or save because: " + e.__repr__())
if not self.data:
#Version 1.1 save file updates
self.data = {"clientOverrideNames":{}, "description":""}
if self.data:
if not "timelineMaximumDuration" in self.data:
self.data["timelineMaximumDuration"] = 5 #5 minutes as sensible default
else:
self.data = {"clientOverrideNames":{}, "description":"", " timelineMaximumDuration":5} #5 minutes as sensible default
logger.info("New/Open complete")
logger.info("New/Open complete")
#TODO: send data
#Data is not send here. Instead the gui calls the getAll message later.
def openFromJson(self, absoluteJsonFilePath):
def openFromJson(self, absoluteJsonFilePath):
with open(absoluteJsonFilePath, "r", encoding="utf-8") as f:
with open(absoluteJsonFilePath, "r", encoding="utf-8") as f:
@ -264,7 +300,7 @@ class DataClient(object):
logger.error(error)
logger.error(error)
if result and "version" in result and "origin" in result and result["origin"] == URL:
if result and "version" in result and "origin" in result and result["origin"] == URL:
if result["version"] > = VERSION:
if result["version"] < = VERSION:
assert type(result) is dict, (result, type(result))
assert type(result) is dict, (result, type(result))
logger.info("Loading file from json complete")
logger.info("Loading file from json complete")
return result
return result
@ -285,8 +321,8 @@ class DataClient(object):
Leave that in for documentation.
Leave that in for documentation.
"""
"""
pass
pass
#def broadcastCallbackFunction(self, ourPath, sessionName, ourClientNameUnderNSM, messagePath, listOfArguments):
#def broadcastCallbackFunction(self, ourPath, sessionName, ourClientNameUnderNSM, messagePath, listOfArguments):
# print (__file__, "broadcast")
# print (__file__, "broadcast")