diff --git a/doc/devices_classes_branch.txt b/doc/devices_classes_branch.txt index c8bc4ee..7283d0d 100644 --- a/doc/devices_classes_branch.txt +++ b/doc/devices_classes_branch.txt @@ -409,52 +409,12 @@ -------------------------------------------------------------------------------- TODO NEXT - KVS - key value store -first line is a command, next lines are data, type is the class to instantiate, -other parameters passed as kwargs. Pass parameter as string, constructor -recognizes hex inside string and base16 parses it to an int first. If no 0x, -it parses as base 10. Record terminated by blank line or EOF. -Multiple blank lines ignored. # at start of command line ignores the whole rec, -allowing user to comment it out temporarily - -ADD tv -type=ENER005 -device_id=0x68b - -First parse is read only open scan of whole file - -If user calls del kvs["tv"] -file opened in read write -first instance of 'tv' found -ADD command changed to IGN (ignore) (same number of chars, so no file size change) -file closed - -When the parser processes it next time, the IGN command just skips -everything until blank line or EOF. - ---- PERSISTENT REGISTRY -* choose a file format for the registry that is human readable like a config file - but also easy to read and occasionally write from a program - - Remember that when restoring the registry in a new run, it can - be told to auto-create device object variables. - -contents (to allow auto creation) - key: user assigned name - values: fsk or ook selector (others in the future) - address(mfrid, productid, deviceid) - date/time added to registry? - -If the user creates a wrapping class around this, we won't store that. -Also no device state will be persisted, it's just a registry of devices -with enough info to auto-create variables for each, correctly bound to -the right device class instances and the correct incoming message router -and outgoing message encoder/modulator. - +implement and test code (write a test harness) ---- NOTIFY, UPDATE, or DATA SEQUENCE? diff --git a/src/energenie/Registry.py b/src/energenie/Registry.py index df5ff87..79e0668 100644 --- a/src/energenie/Registry.py +++ b/src/energenie/Registry.py @@ -63,9 +63,123 @@ return directory[sensor_id] +#----- GENERIC KEY VALUE STORE ------------------------------------------------ + +class KVS(): + DEFAULT_FILENAME='registry.kvs' + + def __init__(self, filename=None, context=None): + if filename == None: filename = KVS.DEFAULT_FILENAME + # context is the namespace consulted to get class variables from when creating objects + self.context = context + self.store = {} + + def load(self): + """Load the whole file into an in-memory cache""" + pass #TODO + # open file for read + # for each line read + # if in command mode + # if blank line, ignore it + # else not blank line + # split line, first word is command, second word is the key + # remember both + # change to data mode + # else in data mode + # if not blank line + # grab key=value + # add to temporary object + # else blank line + # process command,key,values + # now eof + # process command,key,values, if it command is not empty + # close file + + def process(self, command, key, values): + """Process the temporary object""" + pass #TODO + # getattr method associated with the command name, error if no method + # pass the key,values to that method to let it be processed + + def ADD(self, key, values): + """Add a new item to the kvs""" + # The ADD command process the next type= parameter as the class name in context + # all other parameters are read as strings and passed to class constructor as kwargs + pass #TODO + # add key=values to the in memory object store + # open file for append + # write ADD command with key + # for all keys in value + # write k=v + # close file + + def IGN(self, key, values=None): + """Ignore the whole record""" + # The IGN command is the same length as ADD, allowing a seek/write to change any + # command into IGN without changing the file size, effectively patching the file + # so that the record is deleted. + pass # There is nothing to do with this command + + def DEL(self, key, values=None): + """Delete the key from the store""" + # The DEL command deletes the rec from the store. + # This is useful to build temporary objects and delete them later. + # There is no need to write this to the file copy, we're processing the file + pass #TODO + # find key in object store, delete it + + def __getattr__(self, key): + return self.store[key] + + def __setattr__(self, key, value): + self.store[key] = value + self.append(key, value) + + def __delitem__(self, key): + del self.store[key] + self.remove(key) + + def append(self, key, values): + """Append a new record to the persistent file""" + pass #TODO + # open file for append + # write ADD key + # for all values + # write k=v + # close file + + def remove(self, key): + """Remove reference to this key in the file, and remove from in memory store""" + pass #TODO + # open file for read write + # search line at a time, process each command + # when we find the command 'ADD key' + # reseek to start of line + # write overwrite ADD with IGN + # keep going in case of duplicates + # close file + + def rewrite(self): + """Rewrite the whole in memory cache over the top of the external file""" + # useful if you have updated the in memory copy only and want to completely regenerate + pass #TODO + # create file new, for write only + # for all objects in the store by key + # get value + # write ADD command key + # for all values + # write k=v + # write blank line + # close file + #----- NEW DEVICE REGISTRY ---------------------------------------------------- + + + + + # Done as a class, so we can have multiple registries if we want. # TODO: file format? platform dependent database format, like dbm but there is