no change, just more pep8-friendly, and use dict comprehension
[plcapi.git] / PLC / Accessor.py
1 #
2 # Thierry Parmentelat - INRIA
3 #
4 #
5 # just a placeholder for storing accessor-related tag checkers
6 # this is filled by the accessors factory
7 #
8 # NOTE. If you ever come to manually delete a TagType that was created
9 # by the Factory, you need to restart your python instance / web server
10 # as the cached information then becomes wrong
11
12 from PLC.Logger import logger
13
14 from PLC.TagTypes import TagTypes, TagType
15 from PLC.Roles import Roles, Role
16
17 # implementation
18 class Accessor(object):
19     """
20     This is a placeholder for storing accessor-related tag checkers.
21     Methods in this class are defined by the accessors factory
22
23     Class is implemented as a singleton, so we can cache results over time
24     """
25
26     _instance = None
27
28     tag_locators = {}
29
30     def __init__(self, api):
31         self.api = api
32         # 'tagname'=>'tag_id'
33         self.cache = {}
34         self.hash_name_to_role = {role['name']: role for role in Roles(api)}
35
36     def has_cache(self, tagname):
37         return self.cache.has_key(tagname)
38
39     def get_cache(self, tagname):
40         return self.cache[tagname]
41
42     def set_cache(self, tagname, tag_type):
43         self.cache[tagname] = tag_type
44
45     def locate_or_create_tag(self, tagname, category,
46                              description, roles, enforce=False):
47         "search tag type from tagname & create if needed"
48
49         # cached ?
50         if self.has_cache(tagname):
51             return self.get_cache(tagname)
52         # search
53         tag_types = TagTypes(self.api, {'tagname': tagname})
54         if tag_types:
55             tag_type = tag_types[0]
56             # enforce should only be set by
57             # 'service plc start accessors' sequence
58             if enforce:
59                 try:
60                     tag_type.update({'category': category,
61                                      'description': description})
62                     tag_type.sync()
63                     roles_to_add = set(roles).difference(set(tag_type['roles']))
64                     for rolename in roles_to_add:
65                         tag_type.add_role(self.hash_name_to_role[rolename])
66                     roles_to_delete = set(tag_type['roles']). difference(set(roles))
67                     for rolename in roles_to_delete:
68                         tag_type.remove_role(self.hash_name_to_role[rolename])
69                 except:
70                     logger.exception(
71                         "WARNING, Could not enforce tag type, tagname={}\n"
72                         .format(tagname))
73
74
75         else:
76             # not found: create it
77             tag_type_fields = {'tagname': tagname,
78                                'category':  category,
79                                'description': description}
80             tag_type = TagType(self.api, tag_type_fields)
81             tag_type.sync()
82             for role in roles:
83                 try:
84                     role_obj = Roles(self.api, role)[0]
85                     tag_type.add_role(role_obj)
86                 except:
87                     # xxx todo find a more appropriate way of notifying this
88                     logger.exception("Accessor.locate_or_create_tag: "
89                                      "Could not add role {} to tag_type {}"
90                                      .format(role,tagname))
91         self.set_cache(tagname, tag_type)
92         return tag_type
93
94     # a locator is a function that retrieves - or creates - a tag_type instance
95     @staticmethod
96     def register_tag_locator(name, tag_locator):
97         Accessor.tag_locators[name] = tag_locator
98
99     @staticmethod
100     def retrieve_tag_locator(name):
101         return Accessor.tag_locators[name]
102
103     # this is designed to be part of the 'service plc start' sequence
104     # it ensures the creation of all the tagtypes defined
105     # in the various accessors, and enforces consistency to the DB
106     # it's not easy to have define_accessors do this because at
107     # load-time as we do not have an instance of API yet
108     def run_all_tag_locators(self):
109         for (name, tag_locator) in Accessor.tag_locators.items():
110             tag_locator(self, enforce=True)
111
112 ####################
113 # make it a singleton so we can cache stuff in there over time
114 def AccessorSingleton(api):
115     if not Accessor._instance:
116         Accessor._instance = Accessor(api)
117     return Accessor._instance