SinelaboreRT Header Logo

SinelaboreRT

As simple as possible, but not any simpler!

User Tools

Site Tools


start

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
start [2019/06/17 23:44] – [Latest News] pmuellerstart [2024/03/09 10:15] (current) webmin
Line 1: Line 1:
-{{description>SinelaboreRT Generate efficient source code from UML state diagrams!}}+{{htmlmetatags> 
 +metatag-keywords=(UML StateMachine Codegenerator Codegeneration)  
 +metatag-description=(Code generator to build modern and robust event-driven embedded real-time systems based on hierarchical state machines created with UML tools like Enterprise Architect, UModel, Magic Draw, Papyrus, Cadifra.)}}
  
-====== Generate efficient source code from UML state diagrams and activity diagrams! ======+~~NOTOC~~
  
-===== Build robust event-driven embedded real-time software based on hierarchical state machines. =====+====== Generate production ready source code from UML state diagrams ======
  
 +===== What is Sinelabore? =====
 +Sinelabore enables developers to effectively combine event-driven architecture, hierarchical state machines, model-based design and automatic code generation. A payback is usually given already immediately.
  
-Sinelabore//RT// is specially for embedded software developers. It focuses on just one taskGeneration of readable and maintainable source code from hierarchical UML state diagrams - and activity diagramsWith its unique features the tool covers perfectly the requirements of embedded real-time and low-power application developers. With its C/C++/Python/C# and Swift language generators is also a perfect tool for developers of server or desktop applications.+{{::sinelabore_homepage.svg?600| Image showing the design workflow using the sinelabore code generator for embedded system design.}}
  
----- +===== What can it do for me as a software developer for the IoT or for critical applications? ===== 
-<slider :wiki:slider:howdoesitwork.png> +Many systems are likely candidates for implementation as finite state machinesA system that must sequence a series of actions or that must handle inputs differently depending on the mode it is in is often best implemented as a finite state machineTypical examples are control-logic-oriented applications such as metering, monitoring, workflows and control applicationsFor **IoT applications** where parts of the application are implemented in Java / Python / C# / Lua / Rust / JavaScript / Go or Swift, the code can also be generated in these languages.
-<slider :wiki:slider:builtin_editor_3.7.3.png> +
-<slider :wiki:slider:state_chart_equ.jpg> +
-<slider :wiki:slider:car_window_lift_sm.png> +
-<slider :wiki:slider:ea_landing_oven_sc.png> +
-<slider :wiki:slider:dcf77_decoder_machine.png> +
-<slider :wiki:slider:astah_sysml_machine.png> +
-</slider>+
  
----- +===== What can it do for me as an embedded software developer? ===== 
-====== How does it work? ======+Sinelabore//RT// focus is on generation of readable and maintainable code from flat or hierarchical UML state machine diagrams. With its unique features the tool covers perfectly the requirements of **embedded real-time and low-power application developers** coding in C / CPP. The generated code is independent of CPU and operating system.
  
-Use your favorite modeling tool and generate code from it with an easy-to-use command line tool. Automatic model checks warn from design flaws. Configure the generation process according to your needs. Simulate your model. Generate trace code automatically if needed. All major state diagram features like hierarchical states, regions, history, sub-machines ... are supported.  
  
-**Key Features** +===== How do I use it? ===== 
-  * Generated code has production-qualityIt is based on nested ''switch/case'' and ''if/then/else'' statementsIt is easy to read and understandThere will be no problems when using static code analyzers. +Use your existing favourite modelling tool and generate code from it with an **easy-to-use command line tool**. Or use the [[wiki:manual:editor|built editor]] to create state machines within minutesAutomatic model checks warn from design flaws. Configure the generation process according to your needs. Simulate your modelGenerate trace code automatically if neededAll major state diagram features like hierarchical statesregions, historysub-machines ... are supported.
-  * Can be used with any 8-16- or 32-bit CPUswith or without OS/RTOS. There is no run-time environment needed. +
-  * Fits well in different system designsThe code-generator does not dictate how you design your systemTherefore it is no problem to use the generated code in the context of a real-time operating system or within an interrupt service routine or in a foreground / background system. +
-  * No gap between design and code+
  
-<columns 8050% - -+<WRAP center round download 100%> 
-  * Use the UML tool of your choice: +[[wiki:accepted_download| Download & Try it! There are examples for various UML modelling tools and target languages for having a quick start.   
-    * Cadifra UML Editor +]] 
-    * UModel +</WRAP>
-    * Magic Draw +
-    * Enterprise Architect +
-    * SSC (built in editor) +
-    * Metamill +
-<newcolumn> +
-\\  +
-    * ArgoUML +
-    * astah* / astah SysML +
-    * Visual Paradigm +
-    * Modelio+
  
 +===== Does the Code Generator run on my OS? ===== 
 +The Sinelabore code generator runs on any OS that supports a modern Java Version e.g. Windows, 
 +Linux, macOS or from within a container.
  
-</columns>+===== Can I use the generated code on my embedded platform? =====  
 +{{::jigsaw.png?80 |Icon indicating the codegen flexibility}} By generating code that can be compiled with virtually any compiler, and the ability to integrate with your existing IDE, build process or continuous integration system, the code generator can be quickly integrated into any project. Configuration is stored in a plain text file which allows customisation of generated code to exactly your needs. 
 +  * Generated code has production-quality. It is based on nested ''switch/case'' and ''if/then/else'' statements. It is easy to read, understand and debug if needed. The generated code requires no compiler specific tricks except standard language features. This means that if the worst comes to the worst, you can easily change or expand the code by hand. 
 +  * Can be used with any 8-, 16- or 32-bit CPUs. There is **no run-time environment needed** like with some other solutions. 
 +  * Fits well in different system designs. The code generator **does not dictate how you design your system**. Therefore it is no problem to use the generated code in the context of a real-time operating system (VxWorks, FreeRTOS, embOS, RTEMS, ...) or within an interrupt service routine or in a foreground / background (super loop) system. 
 +  * There will be **no problems when using static code analyzers**. 
 +  * Generated cpp code passes clang-tidy and is cpp11 ready (modernize-*). Set configuration parameters accordingly.
  
 +
 +
 +===== Improve Developer Productivity =====
 +Avoid bugs that can waste countless hours of developer and end-user time before they are found. Developers spend a lot of their time coding state machines by hand. 
 +{{::uhr.png?50 |Icon that shows the time saved when using the sinelabore code generator}}
 +And have to do it whenever the design changes. Sinelabore avoids the error-prone and tedious hand-coding by generating high-quality source code directly from the state machine design document.
 +  * No gap between design and code anymore. The documentation is always up to date.
 +  * Use the UML tool of your choice: [[wiki:landing_pages:cadifra|Cadifra UML Editor]], [[wiki:getting_started:umodel_windows_mingw|UModel]],  [[wiki:toolbox:model-based_testing_of_state_machines_i|Magic Draw]], [[wiki:landing_pages:ea|Enterprise Architect]], [[wiki:news:7june2014|Astah* / astah SysML]], Visual Paradigm, [[wiki:howto:modelio|Modelio]]
 +  * [[wiki:manual:editor|An integrated state diagram]] editor makes it easy to get started and allows you to create state diagrams within minutes. The entry barrier is significantly lower compared to full-fledged UML tools. A series of tutorials (see sidebar) explains step by step how to use the integrated diagram tool.
   *  Use the code generator only for those parts of your software that benefit from state machine modeling and code generation. Use your existing development environment for all the other code. The code-generator does not dictate an “all or nothing” approach as many other commercial tools.   *  Use the code generator only for those parts of your software that benefit from state machine modeling and code generation. Use your existing development environment for all the other code. The code-generator does not dictate an “all or nothing” approach as many other commercial tools.
   * Automatic robustness tests, test-case generation, tracing and simulation   * Automatic robustness tests, test-case generation, tracing and simulation
-  * Extensive manual with getting started section+  * Extensive {{:wiki:downloads:sinelaborert.pdf|Manual}} with getting started section
  
 +===== Secure, On-site Code Generation =====
 +The code generator runs locally on your developer workstations, build servers or continuous integration servers. 
 +{{::schluessel.png?50 | Icon that shows the hat your code is never loaded on any server by the sinelabore code generator}}
 +It does not use an internet connection and will never collect nor submit data, code, statistics, analytics, or any other information from your system over any channel.
  
----- 
  
-====== How to get started? ====== 
-[[wiki:download| Download]] the demo version and install it on you computer. Scan the {{:wiki:downloads:sinelaborert.pdf|manual}} starting with the the "Getting Started" section to get a basic understanding of what the tool can do for you. There is also a basic introduction into state-machines in case you need a refresh.  
  
-Then you have basically two options.  +===== Generate code in the shortest time =====  
-  * Run the examples on your PC. The example folder contains examples for all supported modelling tools and various languages (C, C++, ...). The examples realizes a microwave oven and can be executed and tested. Play with the model and enhance it. Regenerate the code and learn from the warning and error messages.+To get an impression of the powerful capabilities of the tool [[wiki:accepted_download| download]] the demo version. Checkout the examples folder to see the generated code. Follow the "Getting Started" pages on this website. The manual contains a basic introduction into state-machines in case you need a refresh. Read the sections related to your UML tool and the language backend you want to use. If no UML tool is already in place take a look at the [[wiki:manual:editor|built in state machine diagram editor]].  
 + 
 +To run the code you have two options.  
 +  * Run the examples on your PC. The example folder contains examples for all supported modelling tools and various languages (C, CPP, ...). The examples realizes a microwave oven and can be executed and tested. Play with the model and enhance it. Regenerate the code and learn from the warning and error messages.
   * Run examples on a Micro-Controller e.g. a MSP430 evaluation board using Energia. An example with all details is available [[https://github.com/sinelabore/examples/tree/master/EnergiaBlink|on github]].    * Run examples on a Micro-Controller e.g. a MSP430 evaluation board using Energia. An example with all details is available [[https://github.com/sinelabore/examples/tree/master/EnergiaBlink|on github]]. 
-====== What customers say ======+===== Customers and what customers say ===== 
 + 
 +The Sinelabore Code Generator is used world wide by very well known tier 1 companies and OEMs. But also small engineering companies as well as single developers. 
  
 //"Sinelabore has helped me implement  the behavior of a complex, asynchronous system. All the UML 2 elements I needed are available. I like that I don’t have to draw the state machine, then separately implement it and keep these two synchronized; this saves me time and reduces the potential of bugs. The error checking to make sure the state machine is valid is also useful. //"Sinelabore has helped me implement  the behavior of a complex, asynchronous system. All the UML 2 elements I needed are available. I like that I don’t have to draw the state machine, then separately implement it and keep these two synchronized; this saves me time and reduces the potential of bugs. The error checking to make sure the state machine is valid is also useful.
Line 69: Line 76:
  
 //"We like Your Tool, infact we will give intro for another local company next week."// //"We like Your Tool, infact we will give intro for another local company next week."//
 +
 +
 +Study done by "//Laboratory of Model Driven Engineering for Embedded Systems @ CEA in France//" with the title "//Complete Code Generation from UML State Machine//" write in their report " //... without optimization, Sinelabore generates the smallest executable size ,,,//".
  
 ---- ----
  
-====== Using State-Machines in (Low-Power) Embedded Systems ======+===== Using State-Machines in (Low-Power) Embedded Systems ===== 
 +Reactive systems are characterized by a continuous interaction with their environment. They typically continuously receive inputs (events) from their environment and − usually within quite a short delay − react on these inputs. Reactive systems can be very well described with the help of state machines. State machines allow to develop an application in an iterative way. States in the state diagram often correspond to states in the application. The resulting model helps to manage the complexity of the application and to discuss it with colleagues from other departments (and domains).  Details can be added step by step during the development. Even during creation, the Code Generator can check the state diagrams for consistency (Model Check). Its logic can be simulated and tested. This ensures that the state machine behaves as intended. 
 +State machines are very useful for control-oriented applications where attributes such as reliability, code size, power consumption, and real-time behavior are particularly important.  
 + 
 +{{ :statemachinebaseddesign.svg |Embedded software architecture using BSP, Drivers and State Machines}} 
 +===== System Architecture ===== 
 + 
 There are different ways how to integrate state machines in a specific system design. Some design principles are more applicable for developers of deeply embedded systems. Others more relevant for developers having not so tight resource constraints. There are different ways how to integrate state machines in a specific system design. Some design principles are more applicable for developers of deeply embedded systems. Others more relevant for developers having not so tight resource constraints.
 +
 +The Sinelabore//RT// code generator supports you in the creation of the state based control logic. 
 +Generated code fits well in different system designs. The code generator does not dictate how you design your system. Therefore it is no problem to use the generated code in the context of a real-time operating system or within an interrupt service routine or in a foreground / background system.
  
 ==== Using state machines in a main-loop ==== ==== Using state machines in a main-loop ====
-In this design an endless loop — typically the main function — calls one or more state machines after each other.  +In this design an endless loop — typically the main function — calls one or more state machines after each other. 
-{{:wiki:mainloop.png?150 |}} It is still one of the most common ways of designing small embedded systems. The event information processed from the state machines might come from global or local variables fed from other code or IRQ handlers. The benefits of this design are no need for a runtime framework and only little RAM requirements.+{{ :mainloop.svg?150|Using state machines in a main-loop}} 
 + It is still one of the most common ways of designing small embedded systems. The event information processed from the state machines might come from global or local variables fed from other code or IRQ handlers. The benefits of this design are no need for a runtime framework and only little RAM requirements.
  
 The consequences are: The consequences are:
Line 98: Line 119:
 ==== Using state machines in a main loop with event queue ==== ==== Using state machines in a main loop with event queue ====
 This design is like the one presented above. But the state machine receives its events from an event queue. The queue is filled from timer events, other state machines (cooperating machines) or interrupt handlers. This design is like the one presented above. But the state machine receives its events from an event queue. The queue is filled from timer events, other state machines (cooperating machines) or interrupt handlers.
-{{ :wiki:mainloop_ext.png?400|}}+{{ :mainloop_ext.svg?400|Using state machines in a main loop with event queue}} 
 Benefits: Benefits:
   * Events are not lost (queuing)   * Events are not lost (queuing)
Line 119: Line 141:
   * The main loop has to check if events are stored for a state machine in its queue. If there are new events they are pulled from the queue and the state machine is called with the event.   * The main loop has to check if events are stored for a state machine in its queue. If there are new events they are pulled from the queue and the state machine is called with the event.
  
-Example code with two state machines shows the general principle:+++++ Example code with two state machines shows the general principle: |
  
 <code c> <code c>
Line 161: Line 183:
 </code> </code>
  
 +
 +\\
 As indicated in the figure above also other state machines or interrupt handlers might push events to the queue of a state machine. An example how to do this is shown below. As indicated in the figure above also other state machines or interrupt handlers might push events to the queue of a state machine. An example how to do this is shown below.
  
Line 170: Line 194:
  
 </code> </code>
 +++++
 ==== Using state machines in a main loop with event queue, optimized for low power consumption ==== ==== Using state machines in a main loop with event queue, optimized for low power consumption ====
 In low power system designs a key design goal is to keep the processor as long as possible in low power mode and only wake it up if something needs to be processed. The design is very similar to the one described above. The main difference is that the main loop runs not all time but only in case an event has happened. The timer service for the small runtime framework is handled in the timer interrupt. In low power system designs a key design goal is to keep the processor as long as possible in low power mode and only wake it up if something needs to be processed. The design is very similar to the one described above. The main difference is that the main loop runs not all time but only in case an event has happened. The timer service for the small runtime framework is handled in the timer interrupt.
- +\\ 
-A skeleton for the MSP430 looks as follows:+++++ A skeleton for the MSP430 looks as follows: |
  
 <code c> <code c>
Line 213: Line 237:
  
 </code> </code>
 +++++
  
 +\\
 +
 +The following temperature transmitter using a MSP430F1232 header board with just 256 bytes of RAM and 8K of program memory is based on this design principle. 
 +{{ :msp430.svg?500 |Architecture for low power designs based on statemachines for MSP430}}
 +For more information on how to use state-machines in low-power embedded systems see [[wiki:toolbox:using_state_machines_in_low-power_embedded_systems|here]] and [[wiki:toolbox:using_state_machines_in_low-power_embedded_systems_part_ii|here]].
  
 ==== Using state machines in interrupts  ==== ==== Using state machines in interrupts  ====
 Sometimes state dependent interrupt handling is required. Then it is useful to embed the state machine directly into the interrupt handler to save every us. Typical usage might be the pre-processing of characters received by a serial interface. Or state dependent filtering of an analog signal before further processing takes place.  Sometimes state dependent interrupt handling is required. Then it is useful to embed the state machine directly into the interrupt handler to save every us. Typical usage might be the pre-processing of characters received by a serial interface. Or state dependent filtering of an analog signal before further processing takes place. 
-{{ :wiki:irq.png?100|}}+{{ :irq.svg?100|Using state machines in interrupts}}
 Using state machines in an interrupt handler can be useful in any system design. Using state machines in an interrupt handler can be useful in any system design.
  
-For code generation some considerations are necessary. Usually it is necessary to decorate interrupt handlers with compiler specific keywords or vector information , etc. Furthermore interrupt service handlers have no parameters and no return value. To meet these requirements the Sinelabore code generator offers the parameters StateMachineFunctionPrefixHeader, StateMachineFunctionPrefixCFile and HsmFunctionWithInstanceParameters. +For code generation some considerations are necessary. Usually it is necessary to decorate interrupt handlers with compiler specific keywords or vector information , etc. Furthermore interrupt service handlers have no parameters and no return value. To meet these requirements the Sinelabore code generator offers the parameters ''StateMachineFunctionPrefixHeader''''StateMachineFunctionPrefixCFile'' and ''HsmFunctionWithInstanceParameters''
  
-The example below shows an interrupt service routine with the compiler specific extensions as required by mspgcc.+++++ The example below shows an interrupt service routine with the compiler specific extensions as required by mspgcc |
  
 <code c> <code c>
Line 248: Line 278:
 Prefixes for the header and the C file can be specified separately.  Prefixes for the header and the C file can be specified separately. 
  
 +++++
 ==== Using state machines with a real-time operating system ==== ==== Using state machines with a real-time operating system ====
 In this design each state machine usually runs in the context of an own task. The principle design is shown in the following figure. In this design each state machine usually runs in the context of an own task. The principle design is shown in the following figure.
  
-{{ :wiki:task_ab.png?400 |}}+{{ :task_ab.svg?400 |Using state machines with a real-time operating system}}
  
 Each task executes a state machine (often called active object) in an endless while loop. The tasks wait for new events to be processed from the state machine. In case no event is present the task is set in idle mode from the RTOS. In case one or more new events are available the RTOS wakes up the task.  The used RTOS mechanism for event signaling can be different. But often a message queue is used. Events might be stored in the event queue from various sources. E.g. from within another task or from inside an interrupt service routine.  Each task executes a state machine (often called active object) in an endless while loop. The tasks wait for new events to be processed from the state machine. In case no event is present the task is set in idle mode from the RTOS. In case one or more new events are available the RTOS wakes up the task.  The used RTOS mechanism for event signaling can be different. But often a message queue is used. Events might be stored in the event queue from various sources. E.g. from within another task or from inside an interrupt service routine. 
Line 265: Line 295:
   * Need of a real-time operating system (complexity, ram usage, cost …)   * Need of a real-time operating system (complexity, ram usage, cost …)
  
-The evaluation version shows an RTOS example in “other_examples/rtems_rtos”. A microwave oven state machine is embedded into the RTEMS real-time operating system. To compile the example you have to install a full RTEMS build environment. The example was created for the PC386 target.+In the how-to section an example of this [[wiki:howto:rtos|pattern is presented with FreeRTOS]]. The examples below shows code for the [[https://www.rtems.org|RTEMS]] and [[https://www.segger.com/products/rtos/embos/|embOS]].
  
 +
 +++++ Example code for RTEMS |
 <code c> <code c>
 // rtems specific task body // rtems specific task body
Line 317: Line 349:
 } }
 </code> </code>
 +++++
  
- +++++ Example code for embOS RTOS from Segger. |
-Here is a similar example for the embOS RTOS available from Segger. +
 <code c> <code c>
 // state machine instance // state machine instance
Line 363: Line 395:
 } }
 </code> </code>
 +++++
  
----- +\\ 
-====== Latest News ======+==== Events versus Boolean Conditions ==== 
 +Sinelabore supports two basic modes of operation. Either the generated state machines react on events. Only if an event is present a transition is taken (e.g. evDoorClosed, evButtonPressed). Events are eventually send to the state machine using an event queue (see above). Alternatively transitions are triggered by boolean conditions. If a boolean condition is true a state change happens (e.g. DI0==true). The latter one is useful if binary signals should be processed like shown in these two designs ([[wiki:examples:function_blocks_desing|signal shaping function blocks]])([[wiki:examples:plcopen_function_block|PLCOpen function block]]). In this case the state machine runs without receiving a dedicated event. Based on the current state, conditions derived from boolean signals are used to trigger state transitions.
  
-=== 17.6.2019 | New version 3.7.4 === 
-{{: pix_mouse.png?nolink |}}This is a bug-fix version and recommended for all users. For users of the Cadifra tool connection points were added to keep oversight in complex diagrams more easy. Connection points are a well known concept in circuit diagram drawing tools. More details can be found in the sinelabore manual (Cadifra part). 
  
-=== 2.5.2019 | New example for PIC users === 
-{{:pix_paper.png?nolink |}}This tutorial explains the use of state machines with the PIC16F18446 Curiosity Nano board. [[wiki:examples:pic_tutorial|Go on reading ...]] 
  
-=== 1.1.2019 | Info for Java 11 users === 
-{{:pix_paper.png?nolink |}}So that Java found the codegenerator and other jar files one suggested option was so far to to use the ''-Djava.ext.dirs'' command line option. With the latest version of Java this option seems not to exist anymore. In future the classpath must be used e.g. ''java -cp "sinelaboreRT3.7.3/bin/*"  codegen.Main ...'' 
-Please note that using -cp in combination with jar files is not possible. We will change the examples and documentation soon. 
  
-=== 9.9.2018 | New version 3.7.3 === 
-{{: pix_mouse.png?nolink |}} General improvements of the built-in editor. Better usage of wide screen displays - properties are now located on the right side. Bug fixes in undo/redo code. 
-{{:builtin_editor_3.7.3.png?direct&600|}} 
  
-=== 29.12.2017 | Sparx new EA Version === 
-{{:pix_paper.png?nolink |}} Tests of new EA version 13 were successful. If you find any flaws using the codegen with the new version send a bug report please. 
  
-=== 5.11.2017 | New version 3.7.2 === +----
-{{: pix_mouse.png?nolink |}} This is a version with minor bug fixes recommended especially for C++ users. +
- +
-=== 20.9.2017 | QuickStart tutorial on how to use the code generator with Energia on GitHub === +
-{{ :wiki:news:launchpads-msp430-msp-exp430fr5969.png?200|}} +
-{{: pix_mouse.png?nolink |}} [[http://energia.nu|Energia]] is based on the Arduino IDE and is a great IDE for processors form Texas Instruments. The new example on GitHub shows the integration of code generated from the sinelabore code generator into the Energia IDE. What does the demo do: The flash frequency of the green LED on the MSP430FR5969 LaunchPad can be controlled. A minimal timer and queueing lib is used as basis to be prepared for much more challenging tasks. You will see the benefit of state machine modeling over direct coding. Take a [[https://github.com/sinelabore/examples|look at the state diagrams and the generated code here]]! Or download the example and try it out yourself. +
- +
- +
-=== 9.4.2017 | New version 3.7.1 with some small improvements  === +
-{{: pix_mouse.png?nolink |}} Recommended for all C# users and those who use the built in editor/simulator.+
  
-=== 6.11.2016 | Code Generator Features for High-Availability and Safety Systems === 
-{{:pix_paper.png?nolink |}} There is a new article in the "[[wiki:toolbox:features_for_high-availability_and_safety_systems|Designers Toolbox]]" explaining the special features for for High-Availability and Safety Systems offered from the C-backend. 
  
-=== 16.10.2016 | Generate Python Code from State Diagrams === 
-{{:pix_mouse.png?nolink |}} With version 3.7 is is now possible to generate Python code. The state machine code is generated as a Python class. All relevant state machine features are available. Look into the manual for details. The fully working microwave oven code is available in the examples folder. It contains a TKinter GUI wich makes simulation and testing of the generated state machine even simpler.  
  
-=== 24.6.2016 | Windows Executable === 
-{{:pix_paper.png?nolink |}} To make the entry barrier lower for users new to Java there is a windows exe file now. You can directly start with the code generator without setting any class path etc. There are two examples in the examples folder (ending with win32) for your reference. This feature is still experimental. The exe file is available with the latest download. Any feedback is welcome! 
start.1560807873.txt.gz · Last modified: 2019/06/17 23:44 by pmueller

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki