Ph.D Kelvin Nilsen
email: kelvin@aonix.com

Applying Java to the Domain of Hard Real-Time Systems

Keywords: hard real time, safety certification, Java, real-time specification for Java

published or not. The documents may come

Applying Java to the Domain of Hard Real-Time Systems

Introduction

In the traditional information processing domain, Java's high-level support for scalable composition of software modules is among its most highly valued benefits in comparison with C and C++ [START_REF] Nilsen | Applying COTS Java Benefits to Mission-Critical Real-Time Software[END_REF][START_REF] Nilsen | Using Java for Reusable Embedded Real-Time Component Libraries[END_REF].

As an object-oriented programming language, the natural style of programming in Java requires dynamic allocation of various temporary objects. In traditional Java, these objects are allocated in the heap, and their memory is reclaimed automatically by a garbage collection system after the objects are no longer in use. Real-time garbage collectors offering sub-ms preemption latencies are available now from multiple vendors. But real-time garbage collection adds complexity, jitter, and performance overhead that are usually considered inappropriate for hard real-time software. In 2000, the Java Community Process introduced the Real-Time Specification for Java (RTSJ) [START_REF] Bollella | The Real-Time Specification for Java[END_REF]. The RTSJ introduced the notion of stacked memory scopes to serve the temporary allocation needs of hard real-time software. By allocating temporary objects within stacked scopes, the allocation and deallocation becomes deterministic. But using memory scopes is very difficult and error prone. This paper presents an approach that layers additional abstraction and static analysis on top of the RTSJ scope stacks in order to simplify development, ease integration of independently developed components, and enable proofs that code components will not fail because they violate scoped-memory protocols. The approaches are similar to the use of SPARK abstractions with the Ada programming language [START_REF] Barnes | High Integrity Software: The SPARK Approach to Safety and Security[END_REF]. Several important factors combine to motivate the design of this technology:

1. The safety-critical development community desires to leverage commercially popular technologies such as Java rather than specialized niche technologies such as Ada because free-market competition among a broader supplier base delivers higher quality technologies for lower prices.

2. Safety-critical systems need to pass stringent certification requirements. Certification authorities expect proofs that programs function correctly. Certification guidelines also recommend elimination of all dead code, and require independent proofs that any deployed dead code is "deactivated", meaning the code will never be executed. Run-time assignment checks are very problematic, because they add significantly to the burden of proof associated with certification of safety-critical software.

3. The size and complexity of safety-critical software continues to grow. Because the costs of developing and certifying safety-critical software modules are so high, there is increasing pressure to reuse safety-critical modules and their certification artifacts. This demands a strong separation of concerns between independently developed software modules. Traditional whole-program analysis tech-niques are problematic because a small change to a single module may invalidate all of the certification artifacts in the system. For many development organizations, this is cost prohibitive.

4. The desire to reuse safety-critical software modules also scales to the domain of mission-critical systems that do not require safety certification. Whenever there are common functional requirements between safety-and mission-critical systems, the industry generally prefers solutions that allow them to maintain a single implementation of the common functionality. Thus, there is a general desire to allow code written to the stringent safetycritical requirements to be deployed in mission-critical systems which may be larger, more complex, and more dynamic than typical safety-critical deployments. For this reason, there is a need to support dynamic loading of safety-certified realtime modules.

Related Work

Others have examined the challenge of using type systems to make the use of scope-or stack-based memory allocation safe. The Cyclone system seeks to establish a safe alternative to C [START_REF] Hicks | Experience With Safe Manual Memory-Management in Cyclone[END_REF]. Cyclone introduces a type system to support safe stack allocation and deallocation of memory, and combines this with explicit safe deallocation of heap objects, along with automatic conservative garbage collection. A project at MIT has also developed a type system for safe region-based memory management of RTSJ programs [START_REF] Salcianu | A Type System for Safe Region-Based Memory Management in Real-Time Java[END_REF]. Though the MIT system has many similarities with the approach described in this paper, a key difference deals with identification of scope levels in argument lists. The MIT approach requires that every scoped argument be associated with a particular named scope, whereas the approach described in this paper, for the most part, simply identifies that certain referenced objects may reside in scoped memory, without requiring programmers to differentiate one scope from another. For certain special circumstances, the type system described in this paper allows programmers to require that certain incoming scoped arguments reside in more outer-nested scopes, or at the same scope level, as certain other scopes that are relevant to execution of a particular method or constructor. While the MIT approach is capable of describing more complicated algorithms and data structures, the MIT type system imposes far greater restrictions on the generality of individual software modules. This specialization of code interfaces hinders software reuse and increases the volume of code that must be written and certified. A third approach to type-safe scoped memory has been described in reference [START_REF] Andreae | Scoped Types and Aspects for Real-Time Java[END_REF]. This approach makes use of existing aspect-oriented programming tools to analyze and transform stylized Java source code.

Thread Stack Memory Model

This paper's approach to hard real-time Java builds on the notion of scoped memory, but hides the RTSJ APIs that manipulate memory scopes. Instead, hard real-time Java programmers describe their intentions with respect to use of scoped memory by using annotations which can be statically analyzed and enforced at compile time. The hard real-time Java profile also supports the notion of immortal memory, which represents the outer-most memory scope. Objects allocated within the immortal memory region will not be reclaimed.

The need to support temporary memory allocation within real-time programs has been well motivated. At the same time, there is general agreement that developers of hard real-time modules do not require the full generality and flexibility offered by automatic garbage collection.

Within the RTSJ, hard real-time programmers are encouraged to use the LTMemory abstraction. This service makes it possible to allocate new memory within a dynamic scope in time that is proportional to the size of the allocation request. A developer of hard realtime software must face several significant difficulties with the use of this abstraction:

1. Knowing how big to make an LTMemory region in order to reliably support execution of a particular real-time module is quite difficult and error prone. Furthermore, it is non-portable between different compliant RTSJ implementations.

1. Instantiation of an LTMemory region is not a hard real-time operation. There is no bound on how much time this will take, and there is, in fact, no guarantee that a request to instantiate an LTMemory region will succeed even if there is sufficient available memory in the system at the time of the request. This is because memory may become fragmented during the course of a program's execution.

Many RTSJ programmers overlook these difficulties with use of the LTMemory abstraction. They regularly allocate and discard LTMemory objects, and successful execution of test programs instills confidence that the code will work reliably in the field. This is a dangerous practice, because it is not generally possible to test all of the different ways that the allocation pool might become fragmented. Further, the program may not behave the same way if it is moved to a different vendor's compliant RTSJ implementation, or even if the same vendor provides a new maintenance release of the same RTSJ implementation.

RTSJ programmers who understand and appreciate the risks of memory fragmentation find that the only way to reliably and safely use LTMemory abstractions in their hard real-time code is to allocate all of the LTMemory objects that their application might need during initialization of the application. This adds significantly to the difficulty of implementing and maintaining the software, and adds considerably to the amount of memory required for reliable execution of the application since many of the LTMemory instances allocated during startup sit idle throughout most of the program's execution.

This paper's hard-real-time Java profile addresses these issues by providing static analysis tools to determine the amount of memory required to execute particular program modules and by requiring all creation and destruction of scopes to follow a strict LIFO (stack) ordering.

At startup, all of the workspace memory available to support execution of the program is set aside as the run-time stack for the main hard real-time Java thread. If the application needs to support more than a single thread, the main program carves memory from its run-time stack to represent the run-time stacks for each of the threads it spawns. Figure 1 illustrates the organization of the main thread's run-time stack immediately after it has spawned three new threads. This illustration assumes that all three threads were spawned from the same context within the main thread. Note that space has been reserved within the main thread's stack to allow the main thread to continue to populate its run-time stack. Note also that it is essential at this point in the program's execution to know the amount of memory that must be reserved to represent each of the spawned thread's run-time stacks. These stacks need not be the same size. In a typical application, the size of each stack is customtailored to the needs of the given thread.

As execution proceeds, each of the three spawned threads and the main thread will continue to populate their respective stacks. Assume the stack memory is organized as shown in Figure 2 at a subsequent execution point.

The scoped memory usage guidelines, as defined in the RTSJ, allow inner-nested objects to refer to objects residing in more outer-nested scopes, but forbids references that go in the opposite direction. These scope-nesting restrictions guarantee that there will never exist a dangling pointer from an outernested object to an inner-nested memory location that no longer exists because its inner-nested scope has been reclaimed. Given these conventions, any data structures that must be shared between multiple threads need to reside either in immortal memory (the outer-most scope, which is never reclaimed) or must exist within the parent or some other ancestor thread's stack below the point at which the descendent thread was spawned. Shared objects do not necessarily need to exist at the time the subthreads are spawned, but the memory allocation contexts within which the shared objects will eventually be allocated must be set aside and entered within the parent thread's stack before the point at which the child thread is spawned.

A special protocol must be enforced to support these conventions. In particular, whenever an ancestor thread desires to unwind its thread stack beyond the point at which it had spawned descendent threads, it must wait for all of the threads that were spawned from this context to terminate execution before it returns from this context. Otherwise, we might create a situation in which a spawned child thread refers to non-existent objects that once resided within the parent's scope.

This special protocol is enforced by the hard real-time Java compiler. Whenever a method spawns the execution of another thread, the code generator inserts into the finalization code for this method a request to unconditionally wait for termination of the spawned thread. This assures that any scope-resident objects shared between this method and its spawned subthread persists as long as the subthread is running.

Type Attributes

Every reference variable within a scope-safe program is classified as having one or more of the following five attributes.

The scoped attribute marks variables that may, but need not, hold references to objects residing in scoped memory. A variable that does not have the scoped attribute is not allowed to hold references to objects residing in scoped memory.

The immortal attribute marks variables that are known to hold references to immortal memory, or null. The immortal attribute is mutually exclusive with the scoped attribute.

The array attribute is used in combination with the scoped attribute to identify variables that may hold references to arrays residing in scoped memory, the elements of which are considered to be scoped variables. In the case that an element of a scopedarray object is itself a reference to another array, the referenced array is considered to have the scopedarray attribute. Note that some reference arrays may have the scoped attribute without having the array attribute. Such an array may reside in scoped memory, but its array elements must reside in immortal memory.

The captive attribute is used in combination with the scoped attribute to identify variables whose contents cannot be copied into static or instance fields. Captive values are only allowed in local variables and arguments.

The result attribute identifies local reference variables whose value might be returned from the currently executing method.

Meta-Data Annotations

The hard real-time Java system uses Java Standard Edition 5.0 style meta-data annotations to characterize the type attributes associated with method and constructor arguments, method return values, instance variables, and static variables. Marking static variables as @Scoped anticipates that certain classes may be dynamically loaded and unloaded within inner-nested scopes. In these circumstances, a dynamically loaded class might have static variables that refer to objects allocated in more outer-nested scoped memory regions. The annotations described in this section are a small subset of the full set of annotations designed for the scope-safe type system. Space does not allow presentation of the full system. See reference [START_REF] Nilsen | Guidelines for Scalable Java Development of Real-Time Systems[END_REF] for more detail.

The @Scoped annotation applies to instance and static fields, methods, and method and constructor argument declarations to indicate that the corresponding variable has the scoped attribute. When applied to a method, this annotation denotes that the method may return a reference to an object that resides in scoped memory.

The @ScopedThis annotation applies to instance methods and constructors to indicate that within the corresponding method or constructor body, the this variable has the scoped attribute. In order to construct an object within scoped memory, the constructor's this variable must have the scoped attribute.

The following implementation of a complex number abstraction demonstrates the use of both annotations: The @CallerAllocatedResult annotation applies to methods to denote that the caller specifies the context within which the method's return object will be allocated. Consider introduction of a multiply() method into the Complex class as an example of this annotation: This example introduces the @ScopedPure annotation, which is shorthand to denote @ScopedThis and @Scoped for all reference arguments. There are certain situations under which a constructor may desire to return an object which contains references to other more outer-nested objects. Consider, for example, the following code: public class String { @Scoped StringBuilder data; int offset, length; @ScopedThis public String(@Scoped StringBuilder sb, int off, int len) { data = sb 1 ; offset = off; length = len; } }

public
@CallerAllocatedResult @
The type system requires that any @Scoped arguments to a @ScopedThis constructor reside in scopes that are at equal or more outer-nested scope level than the object to be constructed 2 . In most constructor invocations, demonstrating compliance with this requirement is trivial, because the typical object is instantiated in the inner-most scope. However, invocation of constructors from within caller-allocated result methods, for example, may introduce violations of this rule.

In general, the type checker enforces that any @Scoped arguments passed to inner-nested caller-allo-cated result methods, or to a constructor that initializes an object to be returned from a caller-allocated result method originate outside the current method's scope. Through inductive reasoning, the hard realtime Java verifier proves that the @Scoped arguments passed to any @CallerAllocatedResult method and to any constructor reside in scopes that enclose the object to be constructed.

In certain situations, the code that invokes a callerallocated result method may choose to request that the caller-allocated result be placed in immortal memory. In this circumstance, the hard real-time Java verifier requires that all @Scoped arguments passed to the caller-allocated-result method refer to objects residing in immortal memory.

Programmers have an alternative annotation to mark the scoped arguments of constructors and caller-allocated-result methods which might refer to objects residing in scopes that are nested internal to the scope of result object. These arguments are marked with the @CaptiveScoped or @CaptiveScopedThis annotations. The type system forbids constructors from copying captive-scoped arguments to the fields of the newly constructed object.

Another special annotation, @NestedReentrantScope, marks classes for which certain private @Scoped instance variables and all @Scoped arguments sent to, and @Scoped values returned from the instance methods are known by the type system to reside at the same scope that holds this object instance. The type checker enforces these invariants by restricting allocation of the object itself, restricting allocations performed by the object's instance methods, and restricting assignments to the object's private instance fields. This annotation is useful when building data structures to represent, for example, balanced binary trees, arbitrary precision numbers, or variants of the java.util.collections libraries. Space constraints do not allow for a complete description of the type checking associated with use of the @NestedReentrantScope or @ReentrantScope annotations. See reference [START_REF] Nilsen | Guidelines for Scalable Java Development of Real-Time Systems[END_REF] for a more thorough explanation.

2. The complete type system described in reference [START_REF] Nilsen | Guidelines for Scalable Java Development of Real-Time Systems[END_REF] suports an @AllowCheckedScopedLinks annotation, which marks methods and constructors that are allowed to perform assignments that require runtime checks. For methods and constructors that carry this annotation, the type system does not enforce that incoming scoped arguments reside in scopes that are assignment compatible with the constructed object.

Inference of Local Attributes

Though Java 5.0 meta-data annotations may be associated with the declarations of local (auto) variables, these annotations are not preserved in the class file representation. Since a design goal is to enforce the scope-safe type system through byte-code verification, the scope-safe type attributes for local variables must be inferred from context. The type inference system implements the algorithm described below.

In discussing this algorithm, the term "variable" represents temporary locations on the operand stack and dedicated slots within the method's activation frame.

In both cases, a particular variable's lifetime begins when the value of that memory location is first defined, and ends immediately following last use of that memory location's value. By this definition, a given memory location may represent different variables at different times.

1. All local reference array variables are assumed to be captive-scoped-array unless demonstrated otherwise.

2. All other local reference variables are assumed to be captive-scoped unless demonstrated otherwise.

3. If this method returns a reference result, mark each of the variables passed as an operand to the areturn byte-code instruction as having the result attribute. a. If there exists a path by which its value might be copied to a variable that has the result attribute, add the result attribute to the first variable's type description.

In order to assure a consistent interpretation of program semantics, the above analysis is performed on the raw byte-code program before all code optimization.

Note that the algorithm described above may result in changes to the attributes of each local variable. When a local variable's attribute changes, the type inference engine must reconsider the type inference impact of all assignments to that variable. The type inference engine iterates in search of a fixed point. The upper bound on running time is n 3 in the number of local variables and assignments contained within a method. Since most methods contain a relatively small number of local variables and assignments, the execution time is generally tolerable.

Byte-Code Verification

Space constraints limit the discussion of byte-code verification to a high-level overview. A more complete description is available in reference [START_REF] Nilsen | Guidelines for Scalable Java Development of Real-Time Systems[END_REF]. Key concepts are discussed in this section.

Localizing references to scope-allocated objects: To enable compile-time assurance of scope safety, the verifier enforces that the content of a scoped variable is never assigned to another variable that is not scoped. Likewise, it enforces that captive-scoped values are never copied to variables that are not identified as captive-scoped variables.

Assuring scalable composition of modules: An important benefit that has made Java more popular than C and C++ for traditional information processing applications is the ease with which independently developed modules are assembled into large and complex software systems. An important goal of the hard realtime Java design is to assure that the scope semantics of individual modules are clearly identified in the annotated API definitions of the modules. These annotations make it possible for software developers and maintainers to easily determine whether particular modules compose simply by examining the interface declarations for those modules.

To enable scalable software development, the verifier enforces consistency between overriding method declarations in subclasses and superclasses. If, for ple, the superclass definition of a method declares its first argument to have the @Scoped attribute, all subclass implementations of the same method must also declare the first argument to be @Scoped or @Captive-Scoped. And if a method is declared with @ImmortalAllocation, all overridden superclass methods must also carry the @ImmortalAllocation annotation. Also, @Immorta-lAllocation methods and constructors may only be invoked from contexts that are themselves declared with the same annotation. This assures that programmers do not inadvertantly invoke a method that performs allocation in immortal memory.

Work is under way to integrate the byte-code verifier within the Eclipse development environment, as illustrated in Figure 3. When programmers save their files, the Eclipse build system automatically invokes the PERC Pico verifier and any errors detected by the enhanced byte-code verifier are immediately displayed within the Eclipse edit window.

Enabling code patterns that elide scope checks: In safety-critical systems, developers are required to offer certification artifacts that prove to the satisfaction of peer reviewers and certifying authorities that code runs correctly, without abnormal termination because a run-time assignment check detects an illegal assignment. In the vanilla RTSJ environment, a run-time check accompanies every reference assignment and an exception is thrown if the assignment would create a reference from an outer-nested object to an innernested object.

The byte-code verifier is required to perform certain analyses in order to justify that particular assignments are scope safe. For example, when assigning to a @Scoped field of an object, the verifier recognizes the following conditions as not requiring a run-time assignment check:

1. If the object that contains the field to be assigned was just allocated within this thread's inner-most local scope context, the verifier recognizes that any values assigned to this field reside in the same or outer-nested contexts.

2. If the value to be assigned was copied from another reference field (or array element) of the same object, the verifier recognizes that the assigned reference value refers to an object residing in the same or outer-nested context.

3. If the value to be assigned was copied from another reference field (or array element) of an object that was reachable from the object that is being assigned to, the verifier recognizes that the assigned reference value refers to an object residing in the same or outer-nested context.

4. If a reference value being returned from a method was passed in as an argument to the method, or was reachable from one of the objects passed in as arguments to the method, the verifier recognizes that the returned value is visible in the scope of the caller's method.

All of the above-described analyses are based on data-flow analysis of reaching definitions within the More sophisticated byte-code analysis rules deal with more complex software constructs. For example, the verifier enforces at the point of invocation that all of the @Scoped (but not necessarily the @CaptiveScoped) arguments of a method declared with the @CallerAllo-catedResult annotation reside in scopes that enclose the scope that holds the caller-allocated result. By inductive reasoning, the byte-code verifier allows the values of incoming @Scoped arguments to be assigned to the fields of the caller-allocated result object without requiring a run-time assignment check. Similar analysis enables certain check-free assignments within the instance methods of @NestedReentrantScope classes.

Layers of Real-Time Abstraction

In systems that require combinations of hard realtime, soft real-time, and non-real-time it is possible to combine hard real-time Java application components with components deployed on traditional Java or soft real-time Java virtual machines. The mechanism described here allows a hard real-time Java application to be paired with a traditional Java virtual machine, enabling efficient and robust implementation of layered architectures comprised of a combination of soft real-time Java components that use standard edition Java libraries and real-time garbage collection and hard real-time Java components that perform all temporary object allocations with a stack of allocation scopes.

Unlike the wait-free queue mechanism that is provided by the RTSJ, this alternative offers the following benefits:

1. All hard real-time Java components reside in a different virtual name space than all soft real-time (or traditional) Java components. This means there is never any confusion between which class libraries are designed to support the hard real-time execution model (without heap) and which libraries are intended to make use of garbage collection.

2. The protocol prohibits direct sharing of objects between the hard real-time and soft real-time domains. Thus, there is no way for an errant or malicious traditional Java component to synchronize at the wrong time or in the wrong way on a shared hard real-time Java object, thereby introducing priority inversion that could possibly compromise the timeliness constraints of all hard realtime components.

3. Traditional Java components can block, waiting for notification from hard real-time components. The mechanism uses a disciplined form of Java's famil-iar wait/notify services. In order to acquire a lock on a hard real-time Java object, a traditional Java thread must transform itself into a hard real-time thread. As such, the thread is under the full control of the hard real-time Java environment's priority ceiling and priority inheritance implementations. While a traditional Java thread holds a lock on a hard real-time Java object, it is only allowed to execute code that was written by the hard real-time Java developer.

4. Entirely under the control of the hard real-time programmer, selected hard real-time Java threads can block, waiting for notification from traditional Java components. The mechanism also uses a disciplined form of Java's familiar wait/notify services.

5. Besides offering improved encapsulation and separation of concerns, this mechanism avoids wasting high-priority CPU cycles in busy-wait loops and avoids the scheduling latency introduced by waiting for a timer tick in an I/O polling loop.

In comparison with the alternative of writing low-level code in C and combining this with Java by way of the JNI protocol, this alternative mechanism offers:

1. Improved robustness and scalability, because object-oriented protocols are used on both sides of the interface, and byte-code verification ensures that proper protocols are followed on each side of the interface.

2. Improved performance, because the byte-code verification performed on both the soft real-time and hard real-time Java components enables optimizations, including in-lining, that are not possible with JNI.

A hard real-time Java method that can be invoked from a traditional Java virtual machine is known as a traditional-Java method. The hard real-time Java byte-code verifier imposes certain restrictions on traditional-Java methods. These restrictions include

• A traditional-Java method cannot be declared to return its result in the caller's scope.

• A traditional-Java method is not allowed to perform object allocations in immortal memory.

• A traditional-Java method must treat all of its incoming arguments as potentially residing in scoped memory.

Experience

The byte-code verifier for this paper's hard real-time Java system was released commercially in the spring of 2007 as part of the PERC Pico product. The PERC Ultra product, which first shipped in 1997, uses real-time garbage collection to support soft real-time applications. An integration of these two products supports hybrid applications.

The hard real-time library subset of standard edition Java comprises approximately 20,000 lines of reusable real-time Java software. To demonstrate the use of this technology, the author has implemented a proprietary RegionMatch Java benchmark based on algorithms provided by one of our aerospace customers (about 2,500 lines) and a single-board computer simulation involving an interrupt-handling device driver (approximately 4,000 lines). We have also ported several benchmarks to the scope-safe type system, including the FFT benchmark of the Java Grande Forum and and the CaffeineMark benchmark. These experiments have demonstrated that the type system is sufficiently expressive to support scope-safe solutions to a broad variety of real-time programming challenges. Evaluation of several synthetic benchmarks demonstrates that hard real-time Java programs run significantly faster than traditional Java. This is because they incur neither the run-time overheads of real-time garbage collection, nor the run-time overheads of scope enforcement.

The effort required to convert vanilla RTSJ and vanilla Java code to the scope-safe type system is facilitated by enforcement of the scope-safe type system. The porting process typically consists of: This is a very different process than the typical effort required to port traditional Java to vanilla RTSJ. In order to port traditional Java code, programmers generally need to understand the program's memory allocation behavior (a non-trivial task for programs that might consist of tens if not hundreds of thousands of lines of code), and need to map all memory allocation operations to appropriate RTSJ scopes (or to the immortal region). With traditional RTSJ porting, programmers are rarely confident that they've done the port correctly, and generally depend on extensive testing to make sure the ported code does not result in illegal assignments, illegal fetches, or scope cycles. Even then, there often remains a lingering uncertainty that testing has not fully exercised all of the paths and data values that might possibly lead to violation of scoped-memory protocol rules.

For many applications, the process of porting to the scope-safe type system is straightforward. For example, the effort required to properly annotate the vanilla Java Grande FFT benchmark was roughly half a day for one software engineer.

In various experiments conducted on this hard realtime Java solution, we have found that Java code structured according to the hard real-time Java conventions executes within 15% of the speed, memory footprint, and determinism of optimized C code. Table 1 compares the efficiency of optimized C with commercial products supporting both soft real-time (PERC Ultra) and hard real-time (PERC Pico) execution of Java. The measured workload was patterned after a real-world defense-system application which a customer used to evaluate the comparative performance of C and Java. Though the Sun HotSpot VM is not designed to support real-time operation, we evaluated the benchmark on Sun HotSpot as well. Interestingly, the average time on Sun Hotspot is even better than optimized C, 294 ns. However, the maximum time for the HotSpot implementation was 8,089 ns and the standard deviation was 228.8 ns. After profiling the code in real time, the Sun HotSpot virtual machine transformed the code, in-lining various method calls in order to optimize the efficiency of the typical path through the code. This optimization approach serves the traditional non-real community very well, but it violates fundamental requirements, such as predictable timing behavior and traceability of code, of most realtime systems.

Also noteworthy was HotSpot's virtual memory consumption of over 250 Mbytes. HotSpot improves upon C speed by profiling the code as it runs and in-lining the implementation of certain key methods in order to optimize their execution on the fly. The reason for the very high standard deviation is because HotSpot's non-real-time garbage collector and dynamic adaptive JIT compiler introduce unpredictable delays into the execution of application code.

In some cases, we have measured that hard real-time Java code runs even faster than comparable handwritten C code. Usually, this occurs when applications allocate large numbers of small temporary objects. The hard real-time Java's scoped-based allocation is much more efficient and deterministic than typical C implementations of malloc and free. Contrast this with compliant full implementations of the RTSJ, which generally run slower and larger than traditional Java, and introduce the complexity of run-time enforcement of scoped memory protocols. This complexity adds significantly to the costs of development, maintenance, and validation of real-time software.

With the ability of hard real-time Java to match C performance, the motivation to use C for the implementation of low-level, performance critical code decreases significantly. Today, the majority of successfully deployed real-time Java applications incorporate a combination of Java, C, and JNI. The C code has been required to implement portions of the system that demand higher throughput, more determinism, or smaller (more economical) memory footprint than is feasible with traditional Java. In some cases, the use of C for low-level software is motivated by a need to directly interface to hardware devices.

Based on many years of experience building embedded systems using the Java language, developers have come to view JNI as the Achilles' heel of their system architectures. All of the security that is such an intrinsic part of the Java platform is compromised at the JNI boundary. Multiple customers have identified this interface as the single most common source of errors in their developed systems. This has motivated embedded Java developers to seek the ability to replace their low-level C code with high-performance Java code.

Besides offering improved abstraction and modularity, the mechanisms described in section 8 also offer significant performance benefits. We experimented with a Java Mandelbrot fractal image application, using "low-level" code to calculate the color of each pixel. Our experiment compared the efficiency of using hard real-time Java vs. optimized C for the low-level code. We found that the differences in performance depends on the size and zoom factor for the image rendered. Certain pixels require more computation than others. Pixels that require a lot of computation favor C over Pico, because the code generated currently by the Pico compiler is not quite as efficient as hand-written C code. The dominant performance fac- tor, however, is the cost of making a JNI invocation vs. the cost of invoking PERC Pico code from within the PERC Ultra environment. This interface is much more efficient than JNI. Consistently, the integrated full-Java solution outperformed the HotSpot/C implementation. For certain rendered images, the total rendering time was over 2.3 times faster with the full-Java solution.

Figure 1 .Figure 2 .

 12 Figure 1. Main Thread Stack After Spawning Three Threads

Figure 3

 3 Figure 3. Hard Real-Time Java Development Environment

Table 1 .

 1 Determinism and Footprint Tradeoffs

		Gnu C
		(gcc -O2) PERC Ultra PERC Pico
	Min (ns)	280	519	314
	Max (ns)	571	1,060	633
	Average (ns)	360	639	392
	Std Dev (ns)	25.52465	48.66415	29.8434
	Memory (MB)	2.32	24	2.5

The hard real-time Java API introduces a variant of StringBuilder that lacks methods to perform mutation on the contents of the character string array. Thus, this implementation is valid.


Uwe Beher
email: uwe.beher@esg.de

Guenter Boenisch
email: guenter.boenisch@continental-corporation.com

Mike Heidrich
email: mike.heidrich@esk.fraunhofer.de

Application area for multiple software product lines in automotive development

Keywords: software product lines, multiple software product lines, SPL Interface Methodology, SPL Interface Data

Since today's well known software product lines (SPL) approaches [1] [2] [4] [5] [6] [7] [8] [9] [12] focus on one single SPL, a methodology for connection and adjustment of multiple product lines is needed. The paper will shortly survey existing processes and methods e.g. FODA [4] for product lines and show adaptations to automotive industry. The focus of the paper will be an adapted process for automotive functional development, which is based on multiple software product lines (MSPL) and particularly regards customer-supplier relationship. It will propose a general SPL interface to manage using MSPL. The interface consists of a SPL Interface Methodology which defines steps for the adjustment of two or more different SPLs as well as a data interface (SPL Interface Data) which defines a data format for the exchange between different SPL tools. The SPL adjustment is demonstrated with a case study of an imaginary advanced driver assistance system called mobilSoft Adaptive Cruise Control (MCC), which consists of three product lines, one for the whole MCC and two for the subsystems linear tracking and traverse control.

Introduction

In automotive functional development aspects like software reliability and productivity draw more and more attention. As a consequence automotive manufacturers and suppliers address improved software engineering processes; introducing software product lines (SPL) increases reuse of software elements and supports accomplishing higher quality at less effort. Implementing improved SPL adaptations for automotive industry was the aim of the research program mobilSoft [START_REF]Softwaretechnik für das Automobil der Zukunft[END_REF]. With the help of latest scientific methods and the experiences of the partners in industry, which were automotive manufacturers and suppliers, purposive solutions for manifold requirements to existing development processes especially for automotive application were found. Among these requirements were short development timelines, less development effort and high quality for each single software element. At the same time development processes are facing increasing variability and complexity of the final products. Four steps were stated as necessary for Automotive OEMs or suppliers to obtain optimized SPLs and to get to a functional demonstrator, which verifies the feasibility of the planned adaptations for the SPL. The first step was an analysis at automotive manufactures and suppliers which lead to global requirements and characteristics for automotive SPLs and supporting processes. The second step was the evaluation of existing methods and tools, which define the technical state of the art, with the requirements found. The third step was evolving a specification of an automotive specific SPL, because existing approaches for SPLs were not particularly made for automotive application. The last step was the setup of a demonstrator, where the appropriate processes and methods were verified. Aim of this last step is not only proofing the concept of an adapted process, but also testing acceptance of comprehensive tool landscapes which are often combined with the introduction of integrated approaches. Among these four steps the analysis of existing methods in the academic environment and available tools on the market turned out to be an appropriate basis for introducing SPLs improvements. In order to adapt existing tools to fit into existing processes, systematic and purposive investigations and determination of efforts are necessary. Especially introducing new methods in existing complex development structures is a critical task and needs well substantiated decision points. Additionally, existing methods and processes were investigated for coverage of the interface between automotive OEMs and suppliers. Because of missing approaches for this important task in automotive development, additional methods were evolved which fit into to existing SPL as an extension of the process framework. The paper will have a main focus on this aspect and will propose an SPL Interface Methodology for adjustment of two or more different SPLs and SPL Interface Data as data interface during parallel development in different organizations. The result of applying these interfaces is a connection of single SPLs to a Multiple Software Product Line (MSPL).

State of the art software product lines

Existing SPL approaches

The arrangement of software development as a software product line is an effective method for increasing reuse in software development and well known in branches with high software share. • The phases of software engineering including scoping, analysis, design or implementation of software components • Main process areas of a SPL, which are domain engineering and application engineering and which have their focus on designing and applying the SPL. Figure 1 shows the coverage of the different approaches in terms of the different software engineering phases and SPL process areas. The range of the coverage extends from very specialized tasks for SPLs up to integrated and comprehensive process models. The application of one of these approaches as whole or partial as individual interpretation of the SPL methodology is, in some instances, realized at automotive manufacturers or suppliers. However, the adaptation of the given approaches to the needs of automotive product development remains being a very complex task and only few parts of the listed approaches fit in the special automotive environment. Thus the following chapter proposes characteristics needed for a SPL to be suitable as an (automotive) MSPL.

Short

Characteristics of an automotive SPL

Regarding [START_REF] Böckle | Software-Produktlinien[END_REF] the SPL is divided into the process areas domain engineering and application engineering. Domain engineering leads to the setup of the software product family and the design of the assets, application engineering deploys the assets of the software product family in order to generate a final product. As preface for a generative approach [START_REF] Czarnecki | Generative Programming[END_REF] the automotive SPL is divided into requirements space and solution space. This results in the following alignments of the SPL: • Domain analysis is the requirements space of the domain engineering • Domain design is the solution space for the domain engineering • Application analysis is the requirements space of the application engineering • Application design is the solution space for the application engineering A common method for organizing and illustrating in the requirements space is the usage of feature models for variant requirements, which include features and the relations between features. Generally a feature model e.g. FODA represents all possible requirements which can be met by the product family.

• A Feature describes the requested product properties from the point of view of a stakeholder, which in the context of a SPL is a customer, developer, manager, investor or supplier. • A feature reflects single requirements or a set of aggregated requirements. • The feature relation describes the relationship between one or more features, which is mandatory, optional or excluding. The selections of features in the feature model during application engineering builds up single products of solution space. The solution space contains a solution model for the domain and the resulting solutions by specific selections in the solution model. The solution selection itself is the result of decisions made along the solution path. The solution model consists of selected assets and their relationship. An asset is the smallest unit of a solution model and can't be divided into smaller units. Assets can be categorized into three types.

• A basic asset is a solution, which can be used in many products without any change. • A customized asset is a basic asset, which can be used in many products and which includes complete calibration possibilities. • A specific asset is a solution for only one product, which is initially introduced into the product family and which aims at utilization for further products. For example an innovation is a specific asset that is implemented in a product for the first time and which, in the case of a successful introduction in the market, is planned to be extended to other products as customized or basic asset. Aligning the design of an asset with an existing product family eases the implementation of the project specific solution into the SPL. The feature-asset relation expresses the relationship between an asset and a feature. Because of a feature being a set of one or more requirements, an assignment between requirements and solution is achieved. The FA Linker (feature asset linker) includes the relationship between chosen features and related assets and thus regards only the relevant subset from the total set of all feature asset relations. For the interface between SPLs, which is described in the following chapter, the FA linker plays a crucial role. With the characteristics and main tasks proposed for SPL software engineering, an appropriate fundament is formed for the specification of an automotive SPL. This leads to connecting different SPLs to a Multiple Software Product Line (MSPL), which is also an automotive requirement.

Connection of software product lines

Fundamentals of connecting product lines

The automotive final product "vehicle" of the OEM is not the result of only one integrated product line. It rather consists of different subsystems with their own product lines and additional single solutions e.g. innovations, which are initially implemented only in few car models. Additionally, each subsystem may be delivered from its own organization, which may be internal or an external supplier. As a result the structure of each product line and the stakeholders mapped to it may differ. 2 shows an OEM with its three platforms "Upper", "Middle" and "Compact". The "Upper" platform itself consists of four assets. "U/M/C" is used in "Upper", "Middle" and "Compact" platform, "U/M" in "Upper" and "Middle" platform, "U/C" in "Upper" and "Compact" platform and finally "U" only in "Upper", which is a single solution. The structure of the supplier in figure 3 is similar in methodology but different for the solution. Its platform "OEM1" consists of "A/B", a common asset for its platform "OEM1" and "OEM2", and a single solution "A". It is used in the "Upper" platform of the OEM in figure 2, but not restricted to it. The supplier has actually no influence on the OEM decision, to put it into "U/M/C", "U/M", "U/C" or "U". And finally, supplier may sell a similar product to another OEM, which will not be regarded in the SPL of the first OEM. As a consequence, even if products from the supplier are used in the platform of the OEM, the configuration of assets which build up the solution space is independent. Furthermore a simple one-to-one match in the solution space where a product "A/B"+"A" may fit into "upper class" vehicles is hardly applicable for automotive industry. In general, separate SPLs exist at different levels of functional hierarchy and depend on their own field of activity. In this case, all individual SPLs need to be synchronized in order to deliver the correct product at the right time. After synchronization, the setup being composed of connected SPLs is stated as a Multiple Software Product Line (MSPL). Figure 4 shows an example for the structure of connected SPLs. The interfaces in this example occur between SPLs internally in one company or externally in many companies. For a functional connection of SPL it is crucial, to align each single SPL in a correct hierarchical order. A layered architecture e.g. EAST-ADL [START_REF] Lönn | Definition of language for automotive embedded electronic architecture[END_REF] is able to structure SPLs hierarchically. In the given example, "SPL A" at supplier may be at the highest level "user" as interface to the customer. But also internal product lines "SPL C" at "cluster" level or "SPL D" at "platform" level need correct alignment to facilitate "SPL A" providing the requested product.

Interfaces of multiple software product lines

As stated in the last chapter an important prerequisite for a MSPL is the synchronization of each SPL to each other in order to align development of the SPLs assets. This paper proposes two main tasks for synchronization:

• Exchange of requirements and design data via a standardized data model "SPL Interface Data" • Adjustment of SPL specific tasks via a general methodology "SPL Interface Methodology" Figure 5 shows the extension of existing data models for general automotive SPLs to implement interface data ("SPL Interface Data") for adaptation to other SPLs. The data exchange for synchronization mainly takes part at an early stage of the development process in a SPL. An appropriate phase is after domain analysis, where each single SPL defines its own reuse concept and asset structure for the product development. Before reaching domain design phase, inputs from all other SPLs complete own basic product requirements by aligning own product application to the requirements of the total product. The SPL interface based on the data models and synchronization tasks needs to cope with a contradiction. On the one hand it shall be as flexible as needed to react on changed constraints after domain design, which at this point typically come from functional implementation. On the other hand the SPL interface shall serve as a consistent backbone for development data. The reason for an architecture mapping is founded in the different SPL approaches, where the connection into the total product architecture is not provided. The architecture mapping verifies the architecture patterns of the single SPLs and assigns each component to its correct place in the product, thus a common understanding of the product is established throughout all SPLs. The allocation list synchronizes the product features and assets and supports the compliance of the requirements to a consistent product design. It deals as configuration support and is a main document for the total product development. The data container is the physical container for design data for combined SPL development which are mainly requirements and assets. It is recommended to structure the container in terms of connected SPLs, this eases each SPL updating their own content during change management. The SPL Interface Methodology describes the process for connecting, adapting and aligning two or more SPLs. It provides initialization and synchronization, which realize a strong connection of the individual development processes to the final product. The SPL Interface Data and the SPL Interface Methodology will be explained in more detail in the following chapters.

Architecture mapping and initialization

A key element of a SPL is a reference architecture, where single products are derived from. In order to connect software product lines, the elements of the reference architectures have to match together to generate a functional total architecture. Reference architectures may be EAST-ADL [START_REF] Lönn | Definition of language for automotive embedded electronic architecture[END_REF] or Car-DL [START_REF] Wild | An architecture-centric approach towards the construction of dependable automotive software[END_REF]. The common base of most reference architectures is a layered system model, where abstract system elements at a higher level are divided into smaller elements at a lower lever which provide more details of the internal structure. The architecture mapping coordinates the different reference architectures at initialization of SPL alignment. It contains methods for the combination of requirements and solution spaces in the correct level of abstraction. Provided, that each individual SPL has a reference architecture, which fits into the general architecture frame for the architecture mapping, the architecture mapping merges all SPLs into one central architecture of the MSPL. As a consequence of all reference architectures being abstract and generic, no SPL has to open its internal detailed architecture to the central architecture of the MSPL for the final product. That is e.g. strategic product plans and customer structures, which are often modeled in internal architectures, stay closed to other competing SPLs. Otherwise areas without any interaction have no significance for the SPL Interface Methodology.

Synchronization and allocation

In a MSPL individual SPLs exchange data mainly during synchronization phase. The data base for the data exchanged is the SPL IF container. Content of the container are requirements, features as sets of requirements, assets, relations of container elements and additional documents, which support version control and asset history. Synchronization is separated into two steps, filling and data adjustment.

During filling phase, all interface data of the other product lines are collected and structured. Basis of the structure is the architecture mapping described previously, which is elaborated during initialization. After all intersections of the product line are identified, all features and assets are put together in the data adjustment phase. As a result, a comprehensive configuration for all features, assets and relations is found, which is valid for realizing the total product. This configuration is a global SPL IF allocation list which contains the relationship between all features and assets involved in the product development. An option for a MSPL demonstration is building up the individual SPLs as hardware independent function, which eases realization of the demonstration. The software platform to be used may be a standardized platform like AUTOSAR, but AUTOSAR is not a prerequisite for the demonstrator. The concept of the stakeholder need for performing the interface for the SPLs does not reflect existing personnel but describes abstract roles and tasks that need to be performed. The mapping of the requested roles to an organizational chart has to be found for each organization individually. The process shown in figure 10 can serve as a template for all automotive organizations in order to perform their own SPL adaptation for a MSPL. In this case study tracking and traverse control are SPLs of two automotive suppliers and the total MCC function a SPL at one OEM. The modeling of all feature models for the problem and solution space was realized with pure::variants [9]. Architecture mapping was evolved within the research project mobilSoft [START_REF]Softwaretechnik für das Automobil der Zukunft[END_REF] by the use of Car-DL [START_REF] Wild | An architecture-centric approach towards the construction of dependable automotive software[END_REF] as a reference architecture. However the shown approach can be used with other architectures, e.g. EAST-EEA-ADL [START_REF] Lönn | Definition of language for automotive embedded electronic architecture[END_REF] as well. The appropriate files for the SPL IF container and the SPL IF allocation list were realized as *.xml-documents according to XMS scheme definitions (XMD) as output of UML based data models. Finally an iterative refinement of the MSPL approach was accomplished.

Conclusion

This paper has provided the structure of SPL Interface Methodology and SPL Interface Data in order to adjust many individual SPLs into one Multiple Software Product Line. The result is a global adjustment list for requirements and solution space of all involved SPLs. The adjustment list is the base of product application in each SPL and is an additional specification at the early stage of a product development process. In automotive applications, after implementing additional requirements emerging from the adjustment list, existing SPL process for product application shall be able to perform SPL product development. The extension of the MSPL has no necessity for special SPL tools and shall be ready to be integrated in most tool supported SPLs.

Glossary

Artifact: A physical piece of information that is used or produced by a software development process. Examples of Artifacts include models, source files, scripts, and binary executable files. Asset: Artifacts in the solution space of a SPL. Examples of assets includes models, code, documentation or test cases.

Domain Engineering:

Software engineering part of a SPL which covers the commonalties and variants of all solutions of a SPL. Feature: A feature describes a product property from the point of view of a stakeholder. A feature is a set of requirements or a single requirement. The relationship between features may be optional or mandatory and which may have constraints. The relationship is graphically described in a feature model.

Layered Architecture:

Base structure of a software product line architecture e.g. Car-DL or EAST-ADL. In a layered architecture a system is structured into different levels of details. MCC: Mobilsoft Cruise Control, the realized demonstrator for a multiple software product line evolved by the project "Teilprojekt 6" of the research program "mobilSoft" under the auspices of the German state of Bavaria. MSPL: Multiple Software product line, connection of many individual and independent software product lines Requirements space:

Focus of all requirements in a software development process. Artifacts of the problem space in a SPL are features. Scoping: Software engineering part of the domain engineering, which consists of product line scoping (identification and description of products), domain scoping (determination of common and different application domains) and asset scoping (planning of the assets for the SPL). SPL: Software product line, a mechanism for software development with focus on reusability, productivity and quality. Solution space: Solving of all requirements in a software development process. Artifacts of the solution space in a SPL are assets

Figure 1 :

 1 Figure 1: Coverage of SPL approaches

Figure 2 :

 2 Figure 2: Example: Structure of an OEM SPL

Figure 3 :

 3 Figure 3: Example: Structure of a supplier SPL Examples for possible product structures with assets at OEM and supplier are depicted in figure 2 and figure 3.Figure2shows an OEM with its three platforms "Upper", "Middle" and "Compact". The "Upper" platform itself consists of four assets. "U/M/C" is used in "Upper", "Middle" and "Compact"

  Figure

Figure 4 :

 4 Figure 4: Connected SPLs

Figure 5 :

 5 Figure 5: Extension of existing SPL data models The SPL Interface Data generally contains design data like • Product requirements • Features of the product as set of requirements • Architecture patterns as reference solutions

  Figure 6 illustrates the role of SPL Interface Data and SPL Interface Methodology in the context of single SPLs implemented into the total product development.

Figure 6 :

 6 Figure 6: Concept of connecting SPLs The part SPL development represents the individual SPL process of an organization like a company or a profit centre in a company. The product development covers the development of the final product as the result of the combination of the individual SPL processes. The SPL interface in between consist of two main elements, the central data container SPL Interface Data and the SPL Interface Methodology in order to synchronize the individual SPL processes. The SPL Interface Data serve as exchange mechanism for design data. It consists of • an architecture mapping • an SPL IF Allocation List • the data container itself

Figure 7 :

 7 Figure 7: Alignment of different reference architectures at initialization Figure 7 shows the result of architecture mapping of individual SPLs. In this figure, three possible SPLs are drawn as bordered shapes. The main objective of the architecture mapping in the SPL Interface Data is to assign each element of the single SPLs to the correct layer of the reference architecture and to requirements or solution space. By overlaying all SPLs valid intersections of all SPLs can be determined. The dark grey area in the middle of all

Figure 8 :

 8 Figure 8: Allocation listFigure8shows the structure of the allocation list, if two SPLs "SPL A" and "SPL B" are connected. Only if the allocation list as a combination of both SPLs contains only viable product solutions, data adjustment phase is finished. Product application design and domain design follow after the adjustment phase. In case of changes in single SPLs are required due to the result of a review, the overall SPL adjustment is triggered again. Figure9shows the resulting package model for a MSPL which contains all data elaborated for connected SPLs. These data are SPL specific data like own reuse, SPL model or Scope or data

Figure 9 :

 9 Figure 9: Package model of a MSPL

Figure 10 : 4 . 2

 1042 Figure 10: Steps for realizing a MSPL During analysis phase of SPL the own standard SPL process or individual interpretation of SPL has to be investigated for adaptations for the MSPL. The result is a general reference model, which supports connection to other SPLs and is able to accomplish own requirements. The verification is achieved by setting up a case study, which combines several methods and SPLs together. The demonstration helps out for analyzing the results of the total process.

Figure 11 :

 11 Figure 11: mobilSoft Cruise Control as a MSPL

  In table 1, a short overview of existing SPL approaches is given. The listed SPL approaches mainly differ in the focus on software engineering tasks. The approaches can be classified in terms of two dominating characteristics.

			recently automotive industry shows increasing
			interest as well.
		Name	Short description
	FODA	Feature	Domain analysis method
		Oriented	based on feature trees [4]
		Domain
		Analysis
	FORM	Feature	Extension of FODA for
		Oriented	domain design [5]
		Reuse Method
	COPA	Component	Approach for setup of
		Oriented	SPLs based on
		Platform	architecture and
		Architecting	component assets [8]
		Method
	FAST	Family-	Total SPL framework [12]
		Oriented
		Abstraction
		Specification
		Translation
	QADA	Quality Driven	Quality oriented software
		Architecture	design [7]
		Design
	PuLSE	Product Line	Generic and
		Software	comprehensive
		Engineering	framework for SPL [2]
	KobrA	Komponen-	Component based
		tenbasierte	software development,
		Anwendungs-	application of PuLSE [1]
		entwicklung
	EAST-	EAST -	Usage of variants in
	ADL	Architecture	automotive software
		Description	design [6]
		Language
	pure::	pure::	SPL modeling tool [9]
	variants	variants	[pure::systems]
		Table 1: Overview of SPL approaches
	It was initially applied e.g. in telecommunication,
	medical equipment or consumer products, but

Acknowledgement

The authors acknowledge the contribution all partners of mobilSoft TP6 to this work which are -in alphabetical order -Audi AG, ESG Elektroniksystemund Logistik GmbH, Siemens VDO Automotive AG and Fraunhofer Einrichtung Systeme der Kommunikationstechnik.


A Mark Grant

B Ken Merrick

HMI automated code generation

In today's automotive infotainment systems a lot of time and effort (= money) is invested in developing the HMI. One of the problems encountered when developing HMI is the necessity to change the spec late on in the development phase which can result in SOP delays. As a result, there is a continuing need to reduce the development time for the HMI. In addition, the OEM's invest in providing their unique branding and have it available across the complete product range offered. Two techniques are invoking interest in this field:

• Cross-platform HMI frameworks

• Multi-platform code generation This paper highlights worked carried out on the second technique -multi-platform code generation.

History of the project

In the beginning of 2000 a development group was created to provide a 'next generation' infotainment system based on Java technology. One of the goals was to provide a flexible HMI framework with support for multi-modality and ease of customisation. It was decided to define the internal format of the HMI specification in XML. Due to budget constraints a design tool that would allow the user to specify the HMI graphically was postponed and so the developers had to 'code' the XML by hand. Once the product was shipped it was decided to provide the design tool and so a few years later a WYSIWYG tool was developed to allow the HMI designer to build the HMI. An additional feature of this tool was to plug in code generators for the different hardware/software target systems.

HMI specification as XML

The industry trend for HMI (layout) specification is to use XML as the meta-language, with the different players providing their own schema. Examples of this are Microsoft (WPF), Adobe (Flex) and the latest being Google (Android). The HMI framework developed for the above system extended the use of XML to include the definition of the overall HMI screen hierarchy and transitions as well as per screen event handling. As XML is a well formed text based language it lends itself to automated 'data-handling' such as runtime parsing, or in our case multi-target code generation.

The KISS Principle

The KISS principle -Keep It Short and Simplemeans make the simple things simple and the complex things possible. The KISS Principle is sometimes cited on a development project to fend off feature creep -a tendency for product or project requirements to increase during development beyond those originally foreseen, leading to features that weren't originally planned and resulting in risk to product quality or schedule. This is a very common design mistake. Users ask for lots of features and developers work hard at delivering those features, but often at the expense of a simple to use application. By simple, we mean "everything should be made as simple as possible, but no simpler" (quote attributed to Albert Einstein). If the interface doesn't make the most common way of performing a task as simple as possible, the interface has failed the user.

Specifying HMI Layout

HMI consists in general, of various graphical objects positioned on the screen in such a way as to convey information to the user in a structured fashion. In most cases this is in the form of Frames, with each frame containing visual elements (widgets). More advanced HMI can provide a better visual appearance using transparent objects, animation and 3D, but generally these can be abstracted to graphical objects laid out on the screen. If we Take a 1000 feet view of the above we can see that the HMI layout can be defined as having at any one time: one screen consisting of one or more frames, each frame containing one or more widgets. Each widget has several properties that helps describe the visual appearance (colour, transparency, text, icon...).

Specifying Event handling/Data binding

An event describes, in sufficient detail, a particular user action. Rather than the program actively collecting user-generated events, the program is notified when an interesting event occurs. Programs that handle user interaction in this fashion are said to be event driven.

Data binding is the process of tying the data in one object to another object. It provides a convenient way to pass data around in an application. Data binding requires a source property, a destination property, and a triggering event that indicates when to copy the data from the source to the destination.

Specifying Screen hierarchy

An applications HMI usually consists of several screens with each set of screens exposing a set of features. As the use selects a feature from one screen, the next screen can propose one or more additional options which are a subset of the current feature. Viewed in this manner we can see that the HMI is composed of a hierarchy of screens and the user navigates within this hierarchy.

Specifying transitions

Navigating from one screen to another is accomplished using event triggers. The result of an event trigger is referred to as a screen transition and can be as simple as displaying one screen after another to providing various animation effects such as fade-in/fade-out. These transitions help provide a workflow when navigating through the HMI and are well defined and consistent throughout the life of the HMI. A special case of transitions which breaks the above workflow is the popup transition. This transition interrupts the normal flow and can result in a new level of navigation (navigating through several screens within the popup context). Once the popup context is terminated the system must return to the original workflow. Note that a system can have several layers of popup and so a context stack must be provided.

Why code generation?

Code generation is the automated building of highlevel code from a description of the requirements of the code. Code generators are split into two major types, passive and active. Passive generation builds a set of code once and does not maintain the code over the long term. Engineers are encouraged to alter the output of a passive generator to meet their requirements. Active generators build code and maintain it through further generation cycles. Code generation provides several advantages over its hand-coded equivalent.

Quality

The quality of generated source code is directly related to the quality of the templates. This is a big advantage because as you increase the quality of the templates over time you will also increase the quality of the entire code base. You can start out with a relatively lightweight pass at the templates and then make them more robust in an iterative process as your gain understanding of the framework and proper error handling. Contrast this process with hand-written code, which will have unreliable quality over time as interest in the project ebbs and flows.

There's another quality advantage, bug fixing.

Fixing 100 hand-coded classes one by one is a lot harder than fixing one template and generating the 100 classes again. Systemic bugs are much more likely to get fixed in generated code bases. In addition, optional enhancements to increase the stability or functionality of the code base are much more likely to happen. For all of these reasons, a generated code base is much more agile and robust than its hand-coded equivalent.

Consistency

Generated code bases are extremely consistent in interface definition. This makes it easier to hand-write code on top of generated APIs. It also makes it easier to build other layers of generated code on top of them.

The easiest and most obvious way to get maintenance changes incorporated is through code generation. The fundamental here is that maintenance programmers can not adequately repeat complex processes, and that instructions become out of date.

Once code is hand-written by an engineer they turn thrit attention to something else and the old code starts to atrophy until someone gives it some more attention. Generated code is continuously maintained by the generator. Every generation cycle replaces the entire generated code base with fresh code. When bugs are fixed in the templates they are propagated across all of the code consistently. So the output code is always better maintained than its hand-coded equivalent.

Productivity

The productivity of the engineering group is unquestionably enhanced by code generation.

The more important productivity increases are tied to the morale improvement you'll see in the engineers working on the project. Code generation applications meet or exceed manually created ones in every aspect. That includes performance, security, source control, and so on.

Code generation using XSLT

XSLT is a template language designed to create any type of text, XML, or Hypertext Markup Language (HTML) output from XML input. Because source code is text output, XSLT can create source code.

The XSLT language presents a new way of thinking because it's explicitly designed from the ground up to apply patterns to information in order to transform information into text. Because metadata is information and source code is text, XSLT does exactly the job presented by code generation. Generally, XSLT processors produce one output file, although an external utility could break the single output file into multiple files. For example, in the case of source code the output file can delimit the beginning and end of each file with markers not likely to occur as part of the source code. Furthermore, the location of each file could be indicated as part of the generation.

The external utility then processes the single output file to produce multiple files in multiple locations.

Similarly, input to the XSLT processor should preferably be one XML file including other XML files.

Code generation using .Net

The .NET framework is a programming model for developing, deploying, and running XML Web services that targets all types of applications. The .NET framework enables building open applications. Every .NET-aware language is compiled to intermediate language code which is executed in Common Language Runtime (CLR). Custom attributes are one of the most innovative features of the .NET framework. They allow everyone to define information which can be applied to classes, methods, parameters, and so on.

Mapping to the target platform

The basic steps to carry out when specifying the code generation are:

• Identify the Layout elements in the target HMI framework • Identify the widget set and properties

• Identify the event handling mechanism

• Identify the HMI menu structure and display management.

• Identify the screen transition mechanism

Once specified the next step is the task of mapping the source HMI schema to the match the final target framework(s). The mapping steps are:

•

Provide template code blocks to represent the creation and layout of the visual elements • Provide template code blocks to set/get the specific widget properties, including type conversion • Implement scripts to output the relevant code blocks when parsing the HMI layout to add the visual elements and set the element properties • Provide template code blocks to map input events to HMI actions (update the screen, send requests to the application...)

•

Provide template code blocks to bind application data to HMI elements • Implement scripts to output the relevant code blocks when parsing the HMI event definitions to handle the input events • Provide code blocks to interact with the target display management framework • Implement scripts to output the relevant code blocks when parsing the HMI menu structure and transition definitions

The above steps cover the majority of cases encountered up until now (true 3D environments excluded).

As an example, let's assume we are targeting a simple Flash framework (Flex based) with the following properties:

• One Frame per screen •

The widget toolkit consists of Text and Button elements (so Lists must be built using Buttons) The resultant file can then be passed on to the Flex compiler to produce a Flash file which can be loaded and tested in a web page.

Testing

The code generation, based on the common XML schema, has so far been tested on the following graphical environments and platforms: • Silverlight (XAML) on Windows XP For each of the above the target widget toolkit had to be developed or an external toolkit used, although a prototype of widget definition using XML is currently being developed.

Conclusion

Using a simple XML specification to define the HMI, we can easily use existing technology to generate HMI for varying platforms and frameworks. With the addition of a WYSIWYG tool this moves the development of the HMI back to the HMI designer and frees up the application developers to do what they are best at -coding, not HMI.

Acknowledgement

I would like to thank Ken Merrick who works within our group for his expert help in analysing the market trends and sanity-checking my work and our various team members who contributed to the development of the HMI frameworks and tooling used throughout our systems.

Glossary

HMI: Human Machine Interface XML: eXtensible Markup Language WYSIWYG:

What You See Is What You Get WPF: Windows Presentation Foundation SDL: Simple Media Layer is a set of portable libraries originally designed for game development that provide graphics/audio capabilities. Android: Android is a software stack for mobile devices that includes an operating system, middleware and key applications. Flex: Adobe® Flex™ is a cross-platform development framework for creating rich Internet applications (RIAs). .Net: .NET is the Microsoft Web services strategy to connect information, people, systems, and devices through software. Integrated across the Microsoft platform, .NET technology provides the ability to quickly build, deploy, manage, and use connected, security-enhanced solutions with Web services.