class Entity(Named): ''' A class modeling the entities in the project repository. ''' fields = refs() database = ref(inv=Database.entities) referents = refs() design = ref() def __init__(self, name, db): super().__init__(name) self.database = db self.idfield = Field('_id') self.revfield = Field('_rev') self.fields.add(self.idfield) self.fields.add(self.revfield) def add_foreign_key(self, name, refent): fk = ForeignKey(name, refent) self.fields.add(fk) return fk def add_field(self, name): field = Field(name) self.fields.add(field) return field def get_field(self, name): for f in self.fields: if f.name == name: return f raise KeyError("Field %s not found in entity %s" % (name, self.name))
class NodeDef: """ The Planning Tool's node definition. """ # some code word code = attr(str) # human-readable description = attr(str) # NODE/ROOT/NID nature = attr(str, nullable=False) required(nature) functionalBlocks = refs(inv=FunctionalBlock.nodeType) descend(functionalBlocks) name = attr(str, nullable=False) motes = refs() nsd = ref() ns_nodedef = ref() # couch entities _id = attr(str) _rev = attr(str)
class NodeType: # index for nodes in this node type index = attr(slice) # the NSD node def nodeDef = attr(NodeDef) # the top-level model castalia_model = ref() # the omnetpp section where this type is configured section = attr(Section) # the node module (dummy) nodes = attr(CastaliaModule) # the communication submodule comm = attr(Communication) def __init__(self, cm, nodeDef, index): self.castalia_model = cm self.nodeDef = nodeDef self.index = index self.nodes = Node(cm.network.base(), 'node', index) self.comm = Communication(self.nodes)
class CouchDesign(Design): ''' A CouchDesign is a couchdb design document for an entity. The design document defines one view for each foreign key and for each uniqueness constraint. ''' # The entity this model is about entity = ref(inv=Entity.design) @property def database(self): return self.entity.database def __init__(self, entity, domain=''): ''' Create the design document. The name of the document is '%s%s_model' %(domain, entity.name). Use domain to avoid collisions. ''' self.entity = entity super().__init__("%s%s_model" % (domain, entity.name)) self.views.add(IndexView('all', entity.idfield)) for fk in entity.fields: if isinstance(fk, ForeignKey): self.views.add(IndexView('by_%s' % fk.name, fk))
class Environment: ''' This class encapsulates the specification of the environment for an NSD. Environment refers to the physical quantities sensed by the sensors in the WSN. ''' nsd = ref() type = attr(str, nullable=False) required(type) physical_measures = attr(set) # These two dicts map physical measure to index # and back pm_index = attr(dict) index_pm = attr(dict) def __init__(self): self.physical_measures = set() self.pm_index = {} self.index_pm = {} def index_physical_measures(self): i = 0 for pm in self.physical_measures: self.pm_index[pm] = i self.index_pm[i] = pm def numPhysicalProcesses(self): return len(self.physical_measures)
class Column: """ Describes a column in a table table with all available columns: _________________________________________________ | module | node | name | label | n_index | data | | | | | | | | | | | | | | | """ def __init__(self, name, origin_table=None, type="varchar"): self.name = name self.origin_table = origin_table self.type = type # the column name name = attr(str) # the table this column will be derived from when creating views # this is needed if the same column appears in two tables that are used to create a view # and the column will be present in the view origin_table = attr(object) # what type (sqlite) of data this column will hold type = attr(str) # the table this column belongs to table = ref()
class FetchField(Named): entity = ref() # the entity this belongs to fkey = attr(Field, nullable=False) # the foreign key to use def __init__(self, name, fkey, multiple=False): super().__init__(name) self.fkey = fkey self.entity = fkey.entity
class ConnectivityMatrix: "The Connectivity matrix from the topology simulator." plan = ref(inv=Plan.connectivityMatrix) rfSimId = attr(str) connectivity = ref_list() descend(connectivity)
class ForeignKey(Field): ''' A foreign-key field. ''' references = ref(inv=Entity.referents) def __init__(self, name, refent): super().__init__(name) self.references = refent
class Field(Named): ''' A field which is compulsory in the object. ''' entity = ref(inv=Entity.fields) constraints = refs() def is_key(self): return self.name == '_id'
class DbDesign(Design): database = ref(inv=Database.design) def __init__(self, db, domain=''): super().__init__("%s_design") self.database = db def to_object(self): return None
class VLMapping: "Combine a physical process' index with " env = ref() pm_index = attr(int, nullable=False) sensor = attr(str) required(sensor) variable = attr(str) required(variable)
class CouchView(Named): ''' A view in a design document. ''' design = ref(inv=CouchDesign.views) @property def resource(self): return "%s/_view/%s" % (self.design.id, self.name)
class FunctionalBlock: blockDefId = attr(str) blockName = attr(str) blockCode = attr(str) isReprogrammable = attr(str) isConfigurable = attr(str) nature = attr(str) noInstances = attr(int) blockInstanceId = attr(int) nodeType = ref()
class Mote: """ A mote represents a wireless sensor, gateway, or other element in the network. """ # node id node_id = attr(str, nullable=False, default=None) required(node_id) json_name('nodeId')(node_id) # the node type determines the hardware used nodeTypeId = attr(str) required(nodeTypeId) # the nodedef object for this mote nodeType = ref(inv=NodeDef.motes) #Mote role (ROOT or MOTE) moteRole = attr(str, nullable=False, default=None) required(moteRole) json_name('nodeType')(moteRole) # position of the node in relative coordinates position = attr(Position) json_name('coordinates')(position) required(position) json_filter(lambda coord: Position(*(float(c) for c in coord)))(position) rf_antenna_conf = attr(object) elevation = attr(float, nullable=False, default=0.0) json_name('elevOfGround')(elevation) rx_threshold = attr(float, nullable=False, default=None) json_name('RXthreshold')(rx_threshold) # the Network object network = ref() # The plan that this is read initially from plan = ref()
class Plan: " A PT plan object " nsd = ref(inv=NSD.plan) name = attr(str, nullable=False) NodePosition = refs(inv=Mote.plan) descend(NodePosition) # numbers numOfNodes = attr(int, nullable=False) numOfRoots = attr(int, nullable=False) numOfNidNodes = attr(int, nullable=False) # units of measurement UOMs = attr(object) connectivityMatrix = ref() # couch entities _id = attr(str) _rev = attr(str)
class EClass(Named): ''' A collection of entities with common privileges ''' by_name = {} acl = ref(inv=ACL.eclass) privileges = refs(inv=Privilege.eclass) superclass = ref() subclasses = refs(inv=superclass) def __init__(self, name, superclass=None): super().__init__(name) if superclass is None: self.superclass = AnyEntity else: self.superclass = superclass self.acl = ACL() def __check_acl(self, roles, priv): d = self.acl.match(roles, priv) if d is None and self.superclass is not None: d = self.superclass.__check_acl(roles, priv) if d is None: d = False return d def authorize(self, roles, priv): '''The main method used to determine authorization.''' all_roles = set() for role in roles: all_roles.update(role.implies) return self.__check_acl(all_roles, priv) def allow(self, role, priv): self.acl.allow(role, priv) def deny(self, role, priv): self.acl.deny(role, priv)
class ACEntry: '''Access control entry''' acl = ref() allow = attr(bool, nullable=False, default=True) # allow or deny role = attr(Role, nullable=False) priv = attr(Privilege, nullable=False) def __init__(self, role, priv, allow=True): self.allow = allow self.role = role self.priv = priv def matches(self, roles, priv): return priv in self.priv.implies and self.role in roles
class ACL: eclass = ref() rules = ref_list(inv=ACEntry.acl) def allow(self, role, priv): self.rules.append(ACEntry(role, priv, True)) def deny(self, role, priv): self.rules.append(ACEntry(role, priv, False)) def match(self, roles, priv): for rule in self.rules: if rule.matches(roles, priv): return rule.allow # return first matching rule! return None
class Parameters: "NSD parameters" nsd = ref() # The time reached when the simulation terminates (sec) sim_time_limit = attr(float) required(sim_time_limit) # Simulation time resolution exponent, The default is -9 (nanosec) simtime_scale = attr(int, default=-9) # CPU time limit, simulation stops when reached. The default is no limit. cpu_time_limit = attr(int, default=None) # seed to initialize RNG, or 0 to initialize it randomly random_seed = attr(int, nullable=False, default=0)
class NsNodeDef: """ The NetSim library node definition. """ nodedef = ref(inv=NodeDef.ns_nodedef) app = attr(object) mote = attr(object) sensors = attr(list) routing = attr(object) mac = attr(object) radio = attr(object) # couch entities _id = attr(str) _rev = attr(str)
class Project: "The project object" nsd = ref(inv=NSD.project) # the owner of the project userId = attr(str) # a list of plans (strings) plans = attr(list) name = attr(str) # this is the EPSG used by the mapping tools, always a UTM srs EPSG = attr(str) # couch entities _id = attr(str) _rev = attr(str)
class Network: """The object describing a Wireless Sensor Network """ def __init__(self, nsd): self.nsd = nsd # the NSD nsd = ref() # All nodes motes = refs(inv=Mote.network) def find_mote(self, node_id): """ Return a mote for the given node_id, or None if no such mote exists. """ for mote in self.motes: if mote.node_id == node_id: return mote return None
class ConstraintUnique(Named): ''' Declare a uniqueness constraint on a number of attributes. ''' entity = ref() fields = ref_list(inv=Field.constraints) primary_key = attr(bool, default=False) def __init__(self, name, entity, keys, primary_key=False): super().__init__(name) self.entity = entity self.primary_key = primary_key assert len(keys) > 0 for key in keys: if isinstance(key, str): key = entity.get_field(key) self.fields.append(key) def names(self): return [f.name for f in self.fields]
class Channel: "A channel is an entry to the connectivity matrix" cm = ref(inv=ConnectivityMatrix.connectivity) channelId = attr(int) required(channelId) nodeId1 = attr(int) required(nodeId1) RSSnodeId1 = attr(float) required(RSSnodeId1) TXpowerNodeId1 = attr(float) required(TXpowerNodeId1) nodeId2 = attr(int) required(nodeId2) RSSnodeId2 = attr(float) required(RSSnodeId2) TXpowerNodeId2 = attr(float) required(TXpowerNodeId2) strengthDb = attr(float) required(strengthDb)
class Section: ''' A config section in the omnetpp.ini file ''' # the section name, or None for the general section name = attr(str, nullable=True, default=None) # the supersections (and subsections) extends = ref_list() extended_by = refs(inv=extends) # the top-level model castalia_model = ref() # module declarations for this section modules = attr(list) def __init__(self, cm, name, extends=[]): self.name = name self.castalia_model = cm self.extends = extends self.modules = []
class NSD: ''' Network Simulation Descriptor This class encapsulates a model to be simulated. It does not contain runtime aspects of the simulation execution itself, such as simulation platform, execution engine etc. An NSD has 4 parts: parameters - a descriptor object containing simulation-related parameters environment - a descriptor object used to define the simulation of the environment, such as the measurements of the sensors application - a descriptor object defining the application simulated (network structure, node types, application logic etc) statistics - a descriptor object specifying the results generated from simulations ''' # # DPCM platform attributes # plan_id = attr(str, nullable=False) required(plan_id) project_id = attr(str, nullable=False) required(project_id) name = attr(str, nullable=False) required(name) userId = attr(str, nullable=False) plan = ref() project = ref() nodedefs = refs(inv=NodeDef.nsd) # # Application # # network object network = ref(inv=Network.nsd) hil = ref(inv=HiL.nsd) descend(hil) # # Parameters # parameters = ref(inv=Parameters.nsd) descend(parameters) # # Environment # environment = ref(inv=Environment.nsd) # # Statistics # plots = attr(list) # couch entities _id = attr(str) _rev = attr(str)
class CastaliaModule: ''' This class represents a castalia module or collection of modules. Subclasses of this class can have additional 'parameter' attributes A module is defined by a path and an index. Also, it can have a parent and a list of submodules. The full_name of a module is determined by its name and index, and those of its parents. Examples. net = CastaliaModule(None, 'SN') -> SN (the root network module) CastaliaModule(net, 'node', slice(10,15)) -> SN.node[10..14] node_group = CastaliaModule(None, 'SN.node', slice(2,5)) -> SN.node[2..4] node_group_radio = CastaliaModule(node_group, 'Radio') -> SN.node[2..4].Radio ''' # module tree parent = ref() submodules = ref_list(inv=parent) # The name and index of this submodule in the parent subname = attr(str, nullable=False) # the index can be a number or a range. index = attr(type=(int, slice), nullable=True, default=None) def __base_name(self): if self.parent is None: return self.subname else: return '.'.join([self.parent.full_name, self.subname]) @property def full_name(self): ''' The pathname for this submodule in the NED tree. ''' if self.index is None: return self.__base_name() else: return "%s[%s]" % (self.__base_name(), index_to_text(self.index)) def submodule(self, name, index=None): ''' Create a new generic submodule for this module. ''' return CastaliaModule(self, name, index) def __getattr__(self, name): ''' This method will be called only if there is no proper attribute by the given name. It will look into the ad-hoc submodules and parameters for a match, and will fail if none is found. Note: parameter names are looked up first, so if there is a name collision the parameter will be returned. ''' assert isinstance(name, str) param_map = object.__getattr__(self, '_CastaliaModule__param_map') if name in param_map: return param_map[name] else: # look for submodule by this name for s in self.submodules: if s.subname == name: return s raise AttributeError("Castalia moduel '%s' has no member '%s'" % (self.__class__.__name__, name)) def base(self): ''' Return a CastaliaModule M. The type of M is always CastaliaModule. M has no parent. The name of the module is equal to the full_name of self, without the index, and the index of M is equal to the index of self. Therefore, M.full_name == self.full_name ''' return CastaliaModule(None, self.__base_name(), self.index) def set(self, pname, pvalue): ''' Set a generic parameter for this module. ''' self.__param_map[pname] = pvalue def all_parameters(self): for pname, pvalue in self.__param_map.items(): yield pname, pvalue for param in self.__model_class__.all_attributes: if Param.has(param): yield param.name, getattr(self, param.name, None) def __init__(self, parent, name, index=None): self.__param_map = {} self.parent = parent self.subname = name self.index = index
class HiL: "The nsd configuration for HiL simulation" nsd = ref() node1 = attr(str, nullable=False) node2 = attr(str, nullable=False)
class Database(Named): entities = refs() design = ref()