This file shows the substantive differences between the dynamic version and the static one. We have reused as much of the static model as we could, esentially redone the connections and packaged the module generators of the users, workstations etc. to support the simple book-keeping as we construct a particular network topology. --------- const.rpd --------- In this file we - let the user determine the number of enclaves - introduce an array keeping track of the number of nodes on each network - introduce counters for each of the other entities of the architecture. All these counters are incremented whenever a new entity is created. 5c5,6 < num_enclaves : integer is get_number("Number of enclaves: "); --- > -- > num_enclaves : integer is 2; 13,18d14 < node_number : array [NetID] of ref(integer) is < (0 .. num_enclaves, < default is ref_to(integer, 0)); < COTS_workstation_number : ref(integer) is ref_to(integer, 0); < user_number : ref(integer) is ref_to(integer, 0); < firewall_number : ref(integer) is ref_to(integer, 0); 20,21c16 < lan_number : ref(integer) is ref_to(integer, 0); < enclave_number : ref(integer) is ref_to(integer, 0); --- > ------------ hardware.rpd ------------ In this file we add an internal action to firewalls and workstations, which reports the address of the node on the network. This is an example of how we can embed analysis-oriented information in the log of the model - in this case to easily identify in the log the topology of the networks. 44,46d43 < action start_WS(Net : NetID; Node : NodeID); < initial < start_WS(network_address.net_no, network_address.node_no); 82,84d79 < action start_FW(Net : NetID; Node : NodeID); < initial < start_FW(network_address.net_no, network_address.node_no); ------------ network.rpd ------------ We embed the same kind of logging information in the network modules, as well. 15,17d14 < action LAN_start(net_no : NetID); < initial < LAN_start(net_no); -- report on the startup of the LAN 31d27 < action WAN_start(net_no : NetID); 33,34d28 < initial < WAN_start(net_no); --------------- generators.rpd --------------- This is a new file. It defines a series of functions, one for each type of entity whose placement within the architecture is unknown at compile time (i.e., users, workstations and firwall, since we do not know a priory which local networks they will be allocated to). The functions are all named "next_??" where "??" represents the type of the entity created by the function call. The functions reuse the module generators of the previous (predefined architecture) example, adding just a bit of packaging. The packaging does two things: - it updates the appropriate counter for the kind of entity created. - it *links* the created module to the appropriate environment in order to ensure that events generated by the module are visible to the architcture (and thus can be connected appropriately - workstation to LAN, WAN to firewall, etc.). The reason why we need this is that the modules of the architecture are almost all elements of arrays (see below), and the default linking for event visibility purposes is to the module elaborating the module generation (in this case, the arrays rather than the architecture). The entities defined are: type Environment is root; next_COTS_workstation : function( environ : Environment) return COTSWorkstation next_user : function (environ : Environment) return User next_firewall : function(environ_ : Environment) return Firewall next_local_area_network : function(environ: Environment) return LAN --------------- netskeleton.rpd --------------- The changes here are twofold: - Since the architecture topology is determined at runtime, the arrays can no longer be laid out by explicit enumeration of the networks, firewalls, workstations and users. Instead we simply identify that as a default, whenever an array element is accessed the first time, the appropriate function is called which will generate a new module of the appropriate type. The Rapide *default* construct is used to this effect. - Though the connect statements remain as before, we now have to traverse the arrays, explicitly activating the components of the architecture. Since accessing an array element once invokes the default expression (which activates the unit), we simply let a dummy variable walk the arrays. SInce this requires an *initial* statement, we have gone beyond the syntacitc confines of the architecture declaration, and define the netskeleton as a module generator instead. 8c9 < module netskeleton() return root is --- > architecture netskeleton() return root is 11,17c12,14 < < LANs : array (NetID, LAN) is ( < default is next_local_area_network(self)); < FWs : array (NetID, firewall) is ( < default is next_firewall(self)); --- > LANs : array(integer, LAN) is ( > 1 is newLAN(1), > 2 is newLAN(1)); 19,22c16,18 < < WSs : array (integer, COTSWorkstation) is ( < default is next_COTS_workstation(self)); --- > FWs : array(integer, firewall) is ( > 1 is newFirewall(mkAddress(1, 0)), > 2 is newFirewall(mkAddress(2, 0))); 25,27c21,32 < users : array (integer, User) is ( < default is next_user(self)); --- > WSs : array(integer, COTSWorkstation) is ( > 1 is newCOTSWorkstation(mkAddress(1, 1)), > 2 is newCOTSWorkstation(mkAddress(1, 2)), > 3 is newCOTSWorkstation(mkAddress(2, 1)), > 4 is newCOTSWorkstation(mkAddress(2, 2))); > > users : array(integer, User) is ( > 1 is newUser(), > 2 is newUser(), > 3 is newUser(), > 4 is newUser()); 30,33d34 < dummy : var root; 62,85d62 < initial < < -- We have to traverse the arrays manually, to access (and thus start) < -- the array elements < < for i : integer in 1 .. num_enclaves < do < dummy := LANs[i]; < end; < < for i : integer in 1 .. num_enclaves < do < dummy := FWs[i]; < end; < < for i : integer in 1 .. num_ws < do < dummy := WSs[i]; < end; < < for i : integer in 1 .. num_ws < do < dummy := users[i]; < end;