Introduction ============ The NED language went through a thorough redesign process for the 4.0 release. Every corner of the language was taken apart, analyzed, and changed if necessary. We wanted a language which lives up to the requirements generated by the appearance of large simulation frameworks, is reasonably extensible, and has a familiar, consistent and concise syntax. The goals did not make it possible to keep the original (3.x) NED syntax. However, because the basic structure of the language remains the same, it is possible to automatically convert existing 3.x NED files into the new syntax. In our experience, automatic conversion works flawlessly, and manual editing is very rarely needed before simulation models are up and running again, with the new syntax. Manual work is needed, obviously, to change the converted NED code to actually take advantage of new language features: default icons, default parameter values, physical unit annotations, inout gates and bidirectional connections, local types, and so on. The most significant changes are: - changed to curly brace syntax - channels became first-class types - added module and channel interfaces, to formalize "like" relationships - added inheritance (may add/refine parametes, gates, submodules, connections) - added inner types (primarily to allow local channel definitions) - added Java-like package system and imports - split "numeric" parameter type to int and double - non-const parameters are now called volatile (const is no more) - expressions can now work with string and XML types as well - const subexpressions (planned) - support for "like" submodule vectors where each module is of different NED type (syntax: foo[5]: <> like IFoo; inifile: **.foo[*].typename = "Foo") - assignment of parameters using wildcard patterns (planned) - added inout gates and bidirectional connections - the in/out "halves" of inout gates may connected independently - more flexible gate vectors (size can be set inside or outside the module type) - added conditional connection groups - added support for default display string (default icon, color etc) for module and channel types - added "properties" (@foo(value[s]) syntax; useful for various annotations) - display string turned into property (@display) - added handling of physical units and their conversion (@unit property) - added support for C++ namespaces (@namespace property) - added support for explicitly specifying C++ implementation class (@class property) - parameter prompt string became @prompt property - further usage of properties envisioned (gate labelling, statistics declaration, etc) - dropped feature: conditional parameters and gatesizes sections (?: can be used instead) - dropped feature: ancestor parameters (violates encapsulation principle) - dropped feature: ref keyword (parameters are now always taken by reference) - no more implicit conversion between bool and long/double Before delving into the details, let us see a code example. This is a variant of the well-known one-FIFO queueing network, in the new NED syntax: // // Generates jobs (messages) with the given interarrival // time and length. Both can be random variates, returning // different values for each job. // simple Source { parameters: volatile double sendIaTime @unit(s); volatile int msgLength = default(100); @display("i=block/source"); gates: output out; } // // A single-server queue // simple Fifo { parameters: volatile double serviceTime @unit(s); @display("i=block/queue;q=queue"); gates: input in; output out; } // // Destroys the packets and collects statistics // simple Sink { parameters: @display("i=block/sink"); gates: input in; } // // Simple queueing network: generator + FIFO + sink. // network FifoNet { submodules: gen: Source { @display("p=89,100"); }; fifo: Fifo { @display("p=209,100"); }; sink: Sink { @display("p=329,100"); }; connections: gen.out --> fifo.in; fifo.out --> sink.in; } Syntax ====== Surely the first thing you have noticed is the curly braces. We changed from using the "endsimple", "endmodule", "endnetwork" and "endchannel" keywords to the curly brace syntax, which is probably more familiar to most readers, and also saves typing. Submodule parameters and gates are now placed within a "submodule body" curly-braced block as well, which makes it clearer for a novice reader to identify where they belong. A side effect is that a submodule's source code would now be one line longer (with the "}" occupying a separate line), but since we also made the "parameters:" keyword optional, the line count is back where it was before. Note that display strings are now specified with the "@display(...)" syntax. The different syntax signifies that the display string has become an instance of something called "property". Properties take the form "@somename(arglist)", and they allow the NED code to be annotated with all sorts of meta-information. A parameters block may actually contain any mix of parameters and module/submodule properties. Properties can be used to annotate parameters and gates as well. For module parameters, note that each module parameter is now terminated with a semicolon (so the parameter list is not a comma-separated list with a semicolon at the end anymore) -- which will probably be a very welcome change. The so far somewhat ah-hoc syntax for declaring parameters and gates has been made consistent as well: now it is " ;" for both. Parameters ========== Types ----- The "numeric" parameter type has been split to "double" and "int". (Int is actually represented as "long" in the C++ code.) When converting a 3.x NED file to the new syntax, the tool will conservatively change "numeric" to "double" everywhere; it is recommmended to review the source afterwards, and manually change "double" to "int" where appropriate. const/volatile -------------- The "const" keyword in 3.x has traditionally caused some confusion for most users. The purpose of "const" was to declare that a parameter is to be evaluated only once, during initialization, and any subsequest read operation should return that value. That is, repeatedly reading a const parameter with the value "exponential(1)" would yield, say, 0.67853375367 all the time, while a similar non-const parameter would produce a random number stream. To make the semantics more intuitive, we changed the syntax: "const" is now the default, and non-const parameters are marked with the "volatile" keyword. That is, we have now "int numHosts" and "volatile double packetInterval". "volatile" conveys the meaning that every time you read the parameter from the simulation, you might get a different value. This is also consistent with the C/C++/Java meaning of the keyword ("should not be cached"), because a volatile module parameter should be re-read each time it is needed, and not cached in a variable. Non-volatile (formerly "const") parameters are converted to a constant value at the beginning of the simulation. Note that in the above Fifo model, the parameters "sendIaTime", "msgLength" and "serviceTime" are all declared volatile. If they are assigned values like "exponential(1)", messages should be generated and services at random intervals. Automatic conversion of NED files performs the "const"/"volatile" conversion correctly. However, because in many 3.x simulation models the authors forgot to write out the "const" keyword, unintended "volatile" keywords may appear in the NED files -- so users should review all "volatile"'s after the conversion, and remove the unwanted ones. Defaults -------- In the 3.x NED, module parameters could only be assigned a value in a submodule's "parameters" section, but not in the module type where the parameter was declared. This has been changed: now you can also supply a value immediately in the parameter declaration. This value may be overwritten where the module is used as a submodule (and also in subclasses, see later). As for the value, you can provide either a concrete value, or a default (using the "default(...)" syntax). The use of default values is that they can be simply applied in the ini files (**.parameterName = default), significantly reducing the size of ini files needed to configure the simulation model. Expressions ----------- Expressions occur at several places in NED files: parameter values, gate vector and module vector sizes, module and gate indexing in connections, connection "for" loop start and stop values, and so on. Expression evaluation has also changed significantly from the 3.x releases. Formerly, expressions involving strings were not supported, and booleans were internally treated as numbers (a'la C). String operations are now possible, and it is also possible to extend the NED language with functions that accept string arguments, or return a string. The same is true for XML parameters: one can create functions that operate on, or return, XML trees. One non backward-compatible change is that now there is no implicit conversion between numeric types and boolean. That is, trying to assign a boolean to an integer or double parameter will result in an error instead of producing "0" or "1"; and similarly, using an int or double as a boolean value is now an error. Such expressions need to be changed to "boolvar ? 1 : 0" and "numericvar==0", respectively. We think that this change will contribute to the cleanliness of NED files, without causing significant inconvenience. Units ----- Expecting a module parameter in one physical unit, then assigning a value in another unit has always been a potential source of errors. (Just think of the legendary Mars probe, which crashed because of mixing up metric and English units of force!) In the new NED language, it is possible to specify which unit a parameter is understood in. This is done by adding "@unit()" to the declaration; for example, "@unit(mW)" for milliwatts, and "@unit(m)" for meters. It is also possible to specify the unit for numeric literals (e.g. "20mW" or "150m") in NED and ini files, and the simulation kernel will assert that the two units match. OMNeT++ knows about certain base units, which are "s" (second), "bps" (bit per second), "B" (byte), "b" (bit), "m" (meter), "W" (watt), and "Hz" (Herz). It also knows about widely used multiples of these units, for example "h" (hour), "ns" (nanosecond), "Gbps" (gigabit per second), "MB" (megabyte), "mW" (milliwatt) etc, and can convert from those into the respective base units. Unrecognized units ("N", "bogomips", etc) are accepted, but there is no conversion for them (so "1kN" is never converted into "1000 N", for example). The rules of assigning a value to a parameter are as follows. - if the value has a unit but the parameter doesn't, an error is raised. - if the parameter has a unit but the value doesn't, the value interpreted as being in the unit of the parameter. - if neither the parameter nor the value has a unit, it is accepted. - if both have units, but the units differ and cannot be converted into each other, it is an error. - if both have units, and they are equal or can be converted into each other ("W" and "mW"), then the conversion takes place if needed. An extra rule is that OMNeT++ 4.0 does not allow numbers with units ("12ms") within arithmetic expressions, only as standalone values. This is a precaution, as OMNeT++ is currently NOT prepared to infer the unit of an expression from its parts, and we do not want a user mistakenly believe it does. For example, "floor(10.2ms)+0.5" is not allowed, because OMNeT++ does not know its unit, and would happily assign it to a parameter with "@unit(s)" as "10.5" -- which would then become "10.5s" (!). "(floor(10.2)+0.5)/1000" is legal. It is also legal to state explicitly the unit of an expression (that is, the unit which the resulting number is to be interpreted in), by appending the unit in square brackets. That is, "(floor(10.2)+0.5) [ms]" is also legal, and will cause the numeric value to be multiplied by 1000 when assigned to a parameter with "unit(s)". One point of non backward compatibility is that multiple numbers and units ("1h 22s 550ms") are no longer supported. This syntax was very rarely used in practice, so very few users are affected. New texT: Unit checking & conversion: unfortunately it can only be done at runtime (cDynamicExpression), because of registered NED functions are not available in NEDXML, and inifile values are hard to analyze statically in NEDXML too. solution: - units checked by cDynamicExpression, during expression evaluation. - NED functions also get units in StkValue, and check them at runtime. - functions: replaceunit(var, "m"); dropunit(var); convertunit(var, "m"); unitof(var) ---------> FIXME change the implementation!!!! Other Properties ---------------- prompt string XXX Conditional sections -------------------- One exotic and rarely used feature of the 3.x NED is not supported in the new NED: conditional parameters sections. An example of what conditional parameters sections looked like: queue: Queue[N]; parameters: capacity = 10; parameters if index==0: capacity = 20; parameters if index==N-1: capacity = 25; In the new NED language, this syntax can be replaced with a single parameter assignment and condition operators (?:) which implement an "if"-ladder ("if..else if...else if..else if.. else"): queue[N]: Queue { capacity = index==0 ? 20 : index==N-1 ? 25 : 10; } The automatic NED conversion tool stops with an error when it encounters conditional sections. The code needs to be manually changed to use "?:", and then it can be converted. Gates ===== Syntax ------ The gate declaration syntax has been made consistent with parameter declarations, so one now writes "input fromTcp;" and "output toTcp;". inout gates ----------- There are now three gate types, "input", "output", and "inout". The introduction of "inout" gates is probably another welcome addition, which makes it possible to create two-way connections. We will talk about it later in detail. Vector gates ------------ Gate vector size may be declared anywhere, and even overwritten anywhere. XXX is it good that you define g[10] here, and overwrite it to g[11] later? is this good??? error-prone!!! Properties ---------- XXX labels that should match Connections =========== Two-way connections can be created by using double-ended arrows, "<-->". The new NED syntaxIn OMNeT++ 3.x release, The syntax of one-way basic connections is still the same as in the 3.x releases. for, if, etc! and two-way connections gate$i, gate$o Properties ========== of parameters, of gates, of modules, etc further usage: declaring statistics, channel behaviour, gate matching, etc! Inheritance =========== The new NED language supports inheritance of modules. rules of inheritance: o for inner types: - can I define an inner type with the same name in subclasses? NO o for properties: - contents will be merged (rules like for display strings: values on same key and same position will overwrite old ones) o for parameters: - type cannot be redefined - value may be redefined in subclasses or place of usage o for gates: - type cannot be redefined - vector size may be redefined in subclasses or place of usage o for gate/parameter properties: - extra properties can be added - existing properties can be overridden/extended like for standalone properties (?) o for submodules: - new submodules may be added, but inherited ones cannot be modified o for connections: - new connections may be added, but inherited ones cannot be modified Network ======= old syntax corresponds to inheritance Channels ======== Inner types =========== Interfaces, "like" ================== Packages ======== Namespaces for C++ classes ========================== // // File comment (copyright statement, whatever...) // // NEW!! withcppclass not needed: to be replaced with @class() // // NAME SCOPES: // - all component type names must be unique // - identifier names within a component must be unique (ie submodule // or inner type cannot be named the same, or the same as a gate or // parameter of the parent module) // THIS MUST BE ENFORCED DURING VALIDATION!!! // // - sizeof() resolution: // IN SUBMODULE SCOPE: // sizeof(foo): must refer to a parent module gate vector, or // sibling submodule vector (there cannot be ambiguity // here, because of the name uniqueness rule above) // NOTE: IT CANNOT REFER TO LOCAL GATE VECTOR !!! // sizeof(this.foo): refers to local gate vector // sizeof(foo.bar): refers to gatevector of sibling submodule // IN CONNECTION SCOPE: // same as above, only sizeof(this.foo) does not make sense // IN [PARENT] MODULE SCOPE: // sizeof(foo), sizeof(this.foo): refers to that module's gatevector // sizeof(foo.bar): makes no sense (thus illegal) // // - "index" operator: it always returns the same (sub)module's index in whose "parameters" // block it is defined. It is NOT POSSIBLE to obtain the parent module's index. // // TODO: // - CHANNELS: two implicit (built-in) channel types exist: IdealChannel, BasicChannel. // Their names can be referenced, ie one can write "channel X extends IdealChannel..." // channel withcppclass BasicChannel { // double delay = 0; // make it "volatile double" ??? // double error = 0; // double datarate = 0; // }; // channel withcppclass IdealChannel { // }; // - Channel inheritance: ultimate base class is always a "channel withcppclass"; // "channel withcppclass X extends Y" is ILLEGAL! (ie "withcppclass" can only be // an ultimate base class). // // - alternative name for "channel withcppclass": "channelbase X {...}" ??? // // - The default base class is BasicChannel. That is: // 1. "--> {...} -->" means "--> BasicChannel {...} -->" // 2. "channel X {...}" means "channel X extends BasicChannel {...}" // // It was considered to force the user spell out "extends BasicChannel", and discarded (inconvenient) // It was considered to make IdealChannel the default, and discarded (impractical and error-prone) // // - This was considered and discarded: "module X extends ASimple" instead of "simple X extends ASimple" // Was proposed because otherwise, if base type's implementation gets changed from // simple to compound, all subclasses will have to be changed. // Idea discarded because the confusion this would create is worse than the problem itself. // // - Note: "simple X extends ASimpleModule": inherits the C++ implementation of ASimpleModule! // (That is, code CANNOT be modified via "extends" -- it always stays the same, // that of the ultimate base class.) Subclass may add new parameters and gates // (if it makes sense...) and set parameters and gate vector sizes. // // - WHAT IF: "simple" would mean "underlying C++ class expected", and // "module" would mean "no underlying C++ class" ? // That is, both "module X extends ASimpleModule" and "simple X extends ASimpleModule" // would be legal and would mean DIFFERENT things. // ("simple X extends ACompoundModule" would still be illegal) // // - "module X extends ACompoundModule": subclass may add further submodules, // gates, connections. Parameters can be added or assigned a value. It may not // remove or modify inherited submodules (there's no syntax for that!) // // XXX TODO: revise all "extends" examples below! // // - Extra params CANNOT be added in submodules and connection channelspecs -- // they can only be added in new types... // // - add constants, modelled after Java's public static final stuff // module Foo { // parameters: // constant string RADIO_FREQ=1.4e9; //Hz // constant string LOOPBACK_IP_ADDRESS="127.0.0.1"; // } // and usage: Foo.RADIO_FREQ. // // - XXX rename "gate" to "port" everywhere? that is the industry standard terminology... // OTOH port is already too overloaded in the INET Framework: TCP port (number), // router port/switch port -- so maybe it is better to leave it as it is // // - XXX rename "interface" to "moduleinterface", to be consistent with "channelinterface" !!! // - XXX find a better name for "channel withclass"!!! // // - XXX do we need to support the "like *" phrase? // // - XXX IBidirectionalChannel, IUnidirectionalChannel: if a module // is "like" them, can it also be "like" channel interfaces...? // // - rules of inheritance: // o for inner types: // - can I define an inner type with the same name in subclasses? NO // o for properties: // - contents will be merged (rules like for display strings: values on // same key and same position will overwrite old ones) // o for parameters: // - type cannot be redefined // - value may be redefined in subclasses or place of usage // o for gates: // - type cannot be redefined // - vector size may be redefined in subclasses or place of usage // o for gate/parameter properties: // - extra properties can be added // - existing properties can be overridden/extended like for standalone properties (?) // o for submodules: // - new submodules may be added, but inherited ones cannot be modified // o for connections: // - new connections may be added, but inherited ones cannot be modified // // - rules of conditionals must be defined exactly! // o for inner types: NO CONDITIONAL // o for properties: YES // o for parameters: only for assignment/property change, but not for declaration // o for gates: only for gatesize/property change, but not for declaration // o for submodules: NO // o for connections: YES // // - XXX is it possible to set a parameter to its default value? e.g. queueCapacity = default; // // - submodule array: was // eth: EtherMAC[n]; // now: // eth[n]: EtherMAC; // Restore old syntax? But it would mean this too: // eth: [n] like EtherMAC; // // // DO WE NEED "like *" ? Not much value in there... // // - CONDITIONAL PARAMETERS, PROPERTIES (@display) AND GATES (GATESIZES) ARE // NOT SUPPORTED. Reasons: // - inefficient at runtime. At network building, one cannot aggregate // parameters on a "prototype module" and then copy it to instances, // but on every instance the whole *procedure* would have to be repeated // (turns declarative impl into imperative...) // - as param values, gate size, param/gate/module/submodule properties may // involve conditionals, they are UNDETERMINABLE with static analysis. // e.g. one cannot tell with static analysis whether a parameter has // been assigned or not. // - strange things like multiple assigments would have to be allow too: // submod: Foo { // parameters: // int a; // a = 3 if index==1; // a = 5; // } // - INCONSISTENCY: with wildcards, FIRST match counts; with conditional // assignments, LAST match counts (it's impossible to do the other way!!!) // - for params and gatesizes, it can be easily rewritten to use "?:" operator: // example: // par = (5 if index<3, 0 otherwise); // generalized (v1 if c1, v2 if c2, v3 if c3, otherwise vdefault); // same as (c1 ? v1 : c2 ? v2 : c3 ? v3 : vdefault) // // - parameter once assigned in base class or type MUST be allowed to be // set to a different value in subclass/place of usage!!! // // RULE: moduleinterface/channelinterface: allow assignments in its parameters/ // allow gatesizes? if parameter is volatile, it will have to be compared as // expression!! // // - VALIDATION: // // package generic.queueing; // default package for following components import "foo.ned"; @someproperty(somekey;otherkey); // file-level property // // Simple FIFO module with a msg/sec processing rate and a finite buffer // capacity. Messages which arrive when the queue is full are dropped. // simple Queue { parameters: // this keyword is optional @display(i="queue"); // default presentation double msgPerSec = default(100); // maybe overridden by another default() int capacity = 100; // fixed value, cannot be overridden int processingTime = exponential(10); // const value returns always the same number int novalue; // no value assigned here volatile int waitTime = exponential(20); // evaluates exp(20) each // time the parameter accessed gates: input in[]; // input gates input in2[5]; // input gates with fixed size output out; // output gate inout thirdGate; } simple VeryFastQueue1 extends Queue // uses "Queue" C++ class { } simple VeryFastQueue2 extends Queue // uses "Queue" C++ class; inherits params/gates { parameters: // this keyword is optional msgPerSec = 1000000; // no type -> sets base class' parameter bool isVeryFast = true; // add new parameter novalue @addedproperty; // attach a property to novalue parameter without assigning a value to it } simple QueueWithFourInputs extends Queue { gates: in[4]; // sets vector gate size to a fixed value input plusOneInput; // adds plus one gate } simple QueueWithNInputs extends Queue { parameters: int numInputs @prompt("Number of input ports"); // add new parameter gates: in[numInputs]; // sets vector gate size to a fixed value } // property syntax: // @propertyname(key="value1","value2"; key="value"; key=5; key=false; key=true; // "value", value, this value, 5, true, false, 10s 531ms) // @propertyname; // propertyname.default="true" // @propertyname(false); // propertyname.default="false" // @propertyname(Hello); // propertyname.default="Hello" // @propertyname(Hello,World);// propertyname.default={"Hello","World"} // @propertyname("egy","ketto",3,more=true); // propertyname.default={"egy","ketto","3","..."}, ~.more="true" // @propertyname(10s 531ms); // propertyname.default="10s 531ms" // @propertyname(starttime=10s 531ms,true); // propertyname.starttime={"10s 531ms",true} // // value in itself is interpreted as belonging to key "default"; and one can override // it with the key=... syntax. // // property representation: // list of key-valuelist pairs; either key or valuelist can be empty // actual storage may be: // hashtable based on key; content is a list (vector) of values // // One property name may occur more than once; runtime effect is that the keys/values will be merged. // Rules of property merging: // - a value at a key/position OVERWRITES earlier value at same key/position. // - the '-' value removes value from that position; one van write literal minus as "-" // example: // @display(i="device/pc",red,10); // @display(i=,,77); ====> results in "i=device/pc,red,77" // @display(i=,-); ====> removes "red", so results is "i=device/pc,,77" // // simple TCP1 { parameters: int mss; // maximum segmentsize int advertisedWindow; // in bytes (Note: normally, NIC queues should be at least this size) string tcpAlgorithmClass1 @enum("TCPTahoe", "TCPReno", "TCPNoCongestionControl", "DumbTCP"); // "..." string tcpAlgorithmClass2 @classname("ITCPAlgorithm"); // alternative syntax: // specify it accepts a class name, and specify base class @kernel(starttime=49); @recordstats[stat1](storeintervall=5; averaging=true); // recording seqNum etc. into output vectors on/off, 'id' is the primary key (must be unique) @recordstats[stat2](storeintervall=9; averaging=false); // recording seqNum etc. into output vectors on/off @display(i="block/transport"); gates: inout toApp[] @labels(TCPControlInfo); // inout; label means "sends/expects msg with TCPControlInfo" inout toNetw @labels(TCPSegment); // inout; label means "sends/expects TCPSegment" } module MobileHost1 { parameters: double x; double y; @display(p=$x,$y); // $paramname is only resolved at runtime } // // // Alternative: gate types as identifiers not strings. // // Undefined or non-matching gatetypes may only cause warning but not error! // // // Messages with attached TCPControlInfo sent/expected // gatetype withTCPControlInfo; // // // Messages of type TCPSegment sent/expected // gatetype TCPSegment; // // // Gate between Ethernet MAC and Ethernet Bus // gatetype EthernetBus; // simple TCP2 { gates: // receiver's labels must be superset of sender's labels (must be able to receive anything sent to it) // inout gates: labels must be exactly the same (superset relation must hold in both directions) inout toApp[] @inlabels(tcp2app) @outlabels(app2tcp) @mustbeconnected; inout toNetw @inlabels(tcp2net) @outlabels(net2tcp); } simple Queue2 { gates: input in[]; // means @labels("*"); output out; // means @labels("*"); } // // modules that can be connected to TCP // interface ITCPApp // no C++ impl; cannot be instantiated { gates: inout toTCP @inlabels(tcp2app) @outlabels(app2tcp); } // // modules that can be connected to UDP // interface IUDPApp { gates: inout toUDP @inlabels(udp2app) @outlabels(app2udp); } simple GenericFTPApp { parameters: double connectInterval; volatile int fileLength; gates: inout toTCP @inlabels(tcp2app) @outlabels(app2tcp); } simple FTPApp extends GenericFTPApp like ITCPApp // or: like TCPApp { parameters: connectInterval = 10s; fileLength = normal(10Mb, 1Mb); // gates & params come from the "extends" base classes; // then they have to match the "like" interfaces. } module FlexibleApp like ITCPApp, IUDPApp // may be connected to *either* TCP or UDP { parameters: double connectInterval = 10s; int fileLength = normal(10Mb, 1Mb); gates: inout toTCP @inlabels(tcp2app) @outlabels(app2tcp); inout toUDP @inlabels(udp2app) @outlabels(app2udp); } interface ITCPUDPApp extends ITCPApp, IUDPApp // both TCP and UDP gates required!!! (e.g. OSPF) { // if there're conflicting gates/params between ITCPApp & IUDPApp ==> error } module OSPFRouting like ITCPUDPApp // connects to *both* TCP and UDP { } property @prop1; // properties must be declared (guard against typos); values for the default key are accepted property @prop2[](foo;bar); // valid key names MUST be declared (only declared keys can be used) //property examples @prop2[one]; @prop2[two](); @prop2[three](,,,foo); @prop2[four](,,,foo,,); @prop2[five](foo=1,2,-,4;bar=40,,0); module Host0 { parameters: @host; // custom properties can serve as "marker interfaces" gates: inout pppPort[] @labels(PPP); inout ethPort[] @labels(Ethernet); // type label implies "EthernetFrame" and UTP inout ethBusPort[] @labels(EthernetBus); // type label implies "EthernetFrame" and coaxial cable submodules: app: <> like TCPApp {}; tcp: TCP1 { mss = 1024; }; ip: IP {}; ppp[sizeof(pppPort)]: PPP {}; connections allowunconnected: app[i].toTcp <--> { @display(c="red"); delay=0; } <--> tcp.toApp; // connect both directions separately (impl: 1 bidir obj, plus 2 child objects on-demand) app[i].toTcp$o --> { @display(c="red"); delay=0; } --> tcp.toApp$i; app[i].toTcp$i <-- { @display(c="red"); delay=0; } <-- tcp.toApp$o; // the right syntax with $i/$o: node1.port$o++ --> node2.port$i++; //XXX and not node1.port++$o node1.port$o[i] --> node2.port$i[i]; tcp.toNetw <--> ip.toTcp; // IP to network interfaces ppp[i].toNetw <--> ip.toIf[i] for i=0..sizeof(ppp)-1; // or: ..... <--> ip.toIf++ where... // network interfaces to parent ppp[i].phy <--> pppPort[i] for i=0..sizeof(ppp)-1; } // // parameter scopes // simple UDP1 { int maxpk; } module Host1 { parameters: double delay; int mss0 = 1000; int mss = 500; int bla = -1; types: simple TCP1X extends TCP1 { int tmp; } submodules: udp: UDP1 { maxpk = 2000; }; tcp: TCP1X { // reserved keywords: "this" // scope on right side: "parent"; one can explicitly write "this." // left side scope is always "this." tmp = 200; mss = mss0; // --> 1000 (parent's) mss = this.tmp; // --> 200 mss = mss; // --> 500 (parent's) mss = udp.maxpk; }; ip: IP {}; connections: tcp.out --> {delay = delay;} --> ip.in; } //property @syntax error $#%$#% channel Eth { parameters: @display(c="red"); @labels(PPP, Eth); double delay1 = 10us; // TODO distance-based delay spec? double delay2 = dist2delay(15m, 200000); double delay3 = dist2delay(15m); // default 200,000 km/s // channel parameters are implemented exactly like module parameters } network Network1 // "network" is exactly as "module", but: not gates { parameters: int a; types: // defines locally used channel and module types FIXME allow types before gates/params as well? channel PPPLink { parameters: @display(c="red"); double delay = 10us; // TODO distance-based delay spec? } module HostX extends Host1 { double linkspeed; } channel SomeLink extends BasicChannel { } submodules: host[100]: HostX {}; connections: host[0].pppPort++ <--> PPPLink <--> host[1].pppPort++; host[0].pppPort++ <--> PPPLink {@display(c="green");} <--> host[3].pppPort++; host[0].pppPort[host[0].destgate] <--> SomeLink {datarate = host[0].linkspeed;} <--> host[3].pppPort++; } simple Node { gates: inout port; } network RandomGraph { submodules: node[100]: Node {}; connections: // brief syntax: node[i].port++ <--> node[j].port++ for i=0..98, for j=i+1..99, if uniform()<0.3; // using a connection group for i=0..98, for j=i+1..99, if uniform()<0.3 { node[i].port++ <--> node[j].port++; }; for i=0..98, for j=i+1..99 { node[i].port++ <--> node[j].port++ if uniform()<0.3; node[i].port++ <--> node[j].port++ if uniform()<0.1; }; for i=0..98, for j=i+1 .. 99, if uniform()<0.3 { node[i].port++ <--> node[j].port++; node[i].port++ <--> node[j].port++; }; for i=0..98, for j=i+1..99 { node[i].port++ <--> node[j].port++ if uniform()<0.3; // dsfdsfdsfsd node[i].port++ <--> // hhhh node[j].port++ if uniform()<0.1; // dfdsfsdfsd //fgdffgfg // fggfdgf node[i].port++ <--> node[j].port++ // dfdsfsdfsd if uniform()<0.1; // dfdsfsdfsd }; for i=0..98 { node[i].port++ <--> node[j].port++ for j=i+1..99, if uniform()<0.3; node[i].port++ <--> node[j].port++ for j=i+1..99, if uniform()<0.1; }; } // // "like" syntax: // module Host2 { parameters: int N; string appType @moduleWithInterface(TCPApp); // "like TCPApp" submodules: apps[N]: like TCPApp {}; // they're all of the same type } module ConfigurableHost { parameters: int N; submodules: app: <> like TCPApp {}; apps[N]: <> like * {}; // uses "typename" virtual parameter, type unchecked FIXME do we need this??? apps[N]: <> like TCPApp {}; // uses "typename" virtual parameter (idea of @type property was discarded) apps[N]: like TCPApp {}; // all modules are the same type (appType); conceptually, <> operator converts string to type //apps[]: like TCPApp {}; // with string arrays; not yet implemented tcp: TCP {}; } network Network2 { submodules: host: ConfigurableHost { N = 5; /apps[0].typename/ = "FTPApp"; // "typename" is a keyword /apps[1..4].typename/ = "WebBrowserApp"; // if Host had a tcp submodule: /tcp*.mss/ = 1024; }; } interface IHost { } network Network3 { submodules: // this is also legal: host: <> like IHost { typename = "FTPApp"; }; // would be the same as: host: FTPApp {}; } channelinterface IPPPChannel { } // "like" syntax for channels network Network4 { parameters: string channelType; submodules: host[100]: Host0 {}; connections: host[0].pppPort++ <--> like IPPPChannel <--> host[1].pppPort++; host[0].pppPort++ <--> <> like IPPPChannel <--> host[1].pppPort++; // how to specify typename? host[0].pppPort++ <--> like * <--> host[1].pppPort++; host[0].pppPort++ <--> <> like * <--> host[1].pppPort++; // how to specify typename? } // channel labels channel PPPLine { parameters: @labels(PPP); // must be a superset of sender gates' labels (must be able to handle at least those pks, and possible more) @display(o="blue",3); } // channel delivers (gate expects) message on reception of first bit or last bit? module EtherMAC1 { gates: inout gate @deliverOnFirstBit; } channel PPPLine1 { parameters: @deliverOnFirstBit; } // channel with or without corresponding C++ class // (@withcppclass property idea was discarded) channel withcppclass PPPLine2 // FIXME find better syntax... { } // modules as channels interface IBidirectionalChannel // <-- predefined (built-in) type { gates: inout a; inout b; } interface IUnidirectionalChannel // <-- predefined (built-in) type { gates: input i; output o; } module PPPLineModule like IBidirectionalChannel // <-- marks PPPLineModule as suitable for use as channel { gates: inout a; inout b; } // example usage of modules as channels network PPPNet { types: simple Node { gates: inout g; } //FIXME how could this be implemented in C++? what's the class name?? submodules: node[10]: Node {}; connections: node[0].g++ <--> PPPLineModule <--> node[1].g++; // would implicitly create submodules named c1,c2,..., and connect them node[0].g++ <--> PPPLineModule <--> node[3].g++; // allowed because PPPLineModule is "like" IBidirectionalChannel //... } // "volatile" parameters simple Gen { parameters: volatile double interArrivalTime; } network Network5 { parameters: volatile double iatime; submodules: gen1: Gen { // there is no submodule group or conditional submodule; nor submodule refinement in subclasses interArrivalTime = 50s 200ms; }; gen2: Gen { interArrivalTime = normal(1,0.3) * const( exponential(30) ); }; gen3: Gen { interArrivalTime = exponential(30); }; gen4: Gen { interArrivalTime = iatime; // by ref interArrivalTime = const(iatime); // by value (evaluated once) }; } // units simple Gen1 { parameters: double time = 10d 4h 34mi 23s 123ms 675us 999ns; // minutes is "mi" not "m"!!!! change strToSimtime()!!!! double datarate = 10Tbps 10Gbps 234Mbps 123Kbps 23bps; double filelengthBytes = 10TB 10GB 100MB 654KB 34B; double distance = 100km 33m 230mm; double time2 @unit(sec); double datarate2 @unit(bps); double filelengthBytes2 @unit(bytes); double distance2 @unit(meter); string className = default("FIFO") @enum("FIFO","LIFO"); double time3 = 10h 30s @unit(sec); }