Add user guide
This commit is contained in:
@@ -60,10 +60,13 @@ mkdir output
|
||||
## Documentation
|
||||
Before attempting to understand this library, we recommend reading the [ns-3 documentation](https://www.nsnam.org/releases/ns-3-40/documentation/). A very good entry point is the [ns-3 tutorial](https://www.nsnam.org/docs/release/3.40/tutorial/html/index.html).
|
||||
|
||||
Next, a set of commented simulation scripts illustrating the use of the libraries can be found at the following paths :
|
||||
Next, a user guide is available in userGuide/.
|
||||
|
||||
Finnaly, a set of commented simulation scripts illustrating the use of the libraries can be found at the following paths :
|
||||
- contrib/ethernet/examples/
|
||||
- contrib/traffic-generator/examples/
|
||||
- contrib/tsn/examples/
|
||||
- contrib/real-device/examples/
|
||||
|
||||
|
||||
## Contact
|
||||
|
||||
20
userGuide/README.md
Normal file
20
userGuide/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Eden-sim - User Guide
|
||||
|
||||
## Introduction
|
||||
This folder contains the user guide for the simulation library. The documentation can be accessed using a browser via book/index.html.
|
||||
|
||||
## Prerequisites
|
||||
The user guide can be read without any prerequisites other than a browser to read the book/index.html
|
||||
|
||||
To build the book, a rust and mdbook installation is required. See [rust installation guide](https://rust-lang.github.io/rustup/installation/index.html) and [mdbook installation guide](https://rust-lang.github.io/mdBook/guide/installation.html).
|
||||
|
||||
## Usage
|
||||
To build the book :
|
||||
```console
|
||||
mdbook build
|
||||
```
|
||||
|
||||
To serve the book :
|
||||
```console
|
||||
mdbook serve --open
|
||||
```
|
||||
4
userGuide/book.toml
Normal file
4
userGuide/book.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[book]
|
||||
title = "Eden-sim - User Guide"
|
||||
authors = ["Quentin BAILLEUL"]
|
||||
language = "en"
|
||||
1
userGuide/book/.nojekyll
Normal file
1
userGuide/book/.nojekyll
Normal file
@@ -0,0 +1 @@
|
||||
This file makes sure that Github Pages doesn't process mdBook's output.
|
||||
241
userGuide/book/404.html
Normal file
241
userGuide/book/404.html
Normal file
@@ -0,0 +1,241 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Page not found - Eden-sim - User Guide</title>
|
||||
<base href="/">
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="favicon-de23e50b.svg">
|
||||
<link rel="shortcut icon" href="favicon-8114d1fc.png">
|
||||
<link rel="stylesheet" href="css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "";
|
||||
const default_light_theme = "light";
|
||||
const default_dark_theme = "navy";
|
||||
window.path_to_searchindex_js = "searchindex-43c2d326.js";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="toc-81bc4348.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Eden-sim - User Guide</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
<h1 id="document-not-found-404"><a class="header" href="#document-not-found-404">Document not found (404)</a></h1>
|
||||
<p>This URL is invalid, sorry. Please use the navigation bar or search to continue.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="elasticlunr-ef4e11c1.min.js"></script>
|
||||
<script src="mark-09e88c2c.min.js"></script>
|
||||
<script src="searcher-c2a407aa.js"></script>
|
||||
|
||||
<script src="clipboard-1626706a.min.js"></script>
|
||||
<script src="highlight-abc7f01d.js"></script>
|
||||
<script src="book-a0b12cfe.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
77
userGuide/book/ayu-highlight-3fdfc3ac.css
Normal file
77
userGuide/book/ayu-highlight-3fdfc3ac.css
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Based off of the Ayu theme
|
||||
Original by Dempfi (https://github.com/dempfi/ayu)
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #191f26;
|
||||
color: #e6e1cf;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #5c6773;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #ff7733;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #ffee99;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-bullet {
|
||||
color: #b8cc52;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-built_in,
|
||||
.hljs-section {
|
||||
color: #ffb454;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-symbol {
|
||||
color: #ff7733;
|
||||
}
|
||||
|
||||
.hljs-name {
|
||||
color: #36a3d9;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #00568d;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #91b362;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #d96c75;
|
||||
}
|
||||
843
userGuide/book/book-a0b12cfe.js
Normal file
843
userGuide/book/book-a0b12cfe.js
Normal file
@@ -0,0 +1,843 @@
|
||||
'use strict';
|
||||
|
||||
/* global default_theme, default_dark_theme, default_light_theme, hljs, ClipboardJS */
|
||||
|
||||
// Fix back button cache problem
|
||||
window.onunload = function() { };
|
||||
|
||||
// Global variable, shared between modules
|
||||
function playground_text(playground, hidden = true) {
|
||||
const code_block = playground.querySelector('code');
|
||||
|
||||
if (window.ace && code_block.classList.contains('editable')) {
|
||||
const editor = window.ace.edit(code_block);
|
||||
return editor.getValue();
|
||||
} else if (hidden) {
|
||||
return code_block.textContent;
|
||||
} else {
|
||||
return code_block.innerText;
|
||||
}
|
||||
}
|
||||
|
||||
(function codeSnippets() {
|
||||
function fetch_with_timeout(url, options, timeout = 6000) {
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),
|
||||
]);
|
||||
}
|
||||
|
||||
const playgrounds = Array.from(document.querySelectorAll('.playground'));
|
||||
if (playgrounds.length > 0) {
|
||||
fetch_with_timeout('https://play.rust-lang.org/meta/crates', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
// get list of crates available in the rust playground
|
||||
const playground_crates = response.crates.map(item => item['id']);
|
||||
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
|
||||
});
|
||||
}
|
||||
|
||||
function handle_crate_list_update(playground_block, playground_crates) {
|
||||
// update the play buttons after receiving the response
|
||||
update_play_button(playground_block, playground_crates);
|
||||
|
||||
// and install on change listener to dynamically update ACE editors
|
||||
if (window.ace) {
|
||||
const code_block = playground_block.querySelector('code');
|
||||
if (code_block.classList.contains('editable')) {
|
||||
const editor = window.ace.edit(code_block);
|
||||
editor.addEventListener('change', () => {
|
||||
update_play_button(playground_block, playground_crates);
|
||||
});
|
||||
// add Ctrl-Enter command to execute rust code
|
||||
editor.commands.addCommand({
|
||||
name: 'run',
|
||||
bindKey: {
|
||||
win: 'Ctrl-Enter',
|
||||
mac: 'Ctrl-Enter',
|
||||
},
|
||||
exec: _editor => run_rust_code(playground_block),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updates the visibility of play button based on `no_run` class and
|
||||
// used crates vs ones available on https://play.rust-lang.org
|
||||
function update_play_button(pre_block, playground_crates) {
|
||||
const play_button = pre_block.querySelector('.play-button');
|
||||
|
||||
// skip if code is `no_run`
|
||||
if (pre_block.querySelector('code').classList.contains('no_run')) {
|
||||
play_button.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// get list of `extern crate`'s from snippet
|
||||
const txt = playground_text(pre_block);
|
||||
const re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
|
||||
const snippet_crates = [];
|
||||
let item;
|
||||
while (item = re.exec(txt)) {
|
||||
snippet_crates.push(item[1]);
|
||||
}
|
||||
|
||||
// check if all used crates are available on play.rust-lang.org
|
||||
const all_available = snippet_crates.every(function(elem) {
|
||||
return playground_crates.indexOf(elem) > -1;
|
||||
});
|
||||
|
||||
if (all_available) {
|
||||
play_button.classList.remove('hidden');
|
||||
play_button.hidden = false;
|
||||
} else {
|
||||
play_button.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function run_rust_code(code_block) {
|
||||
let result_block = code_block.querySelector('.result');
|
||||
if (!result_block) {
|
||||
result_block = document.createElement('code');
|
||||
result_block.className = 'result hljs language-bash';
|
||||
|
||||
code_block.append(result_block);
|
||||
}
|
||||
|
||||
const text = playground_text(code_block);
|
||||
const classes = code_block.querySelector('code').classList;
|
||||
let edition = '2015';
|
||||
classes.forEach(className => {
|
||||
if (className.startsWith('edition')) {
|
||||
edition = className.slice(7);
|
||||
}
|
||||
});
|
||||
const params = {
|
||||
version: 'stable',
|
||||
optimize: '0',
|
||||
code: text,
|
||||
edition: edition,
|
||||
};
|
||||
|
||||
if (text.indexOf('#![feature') !== -1) {
|
||||
params.version = 'nightly';
|
||||
}
|
||||
|
||||
result_block.innerText = 'Running...';
|
||||
|
||||
fetch_with_timeout('https://play.rust-lang.org/evaluate.json', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.result.trim() === '') {
|
||||
result_block.innerText = 'No output';
|
||||
result_block.classList.add('result-no-output');
|
||||
} else {
|
||||
result_block.innerText = response.result;
|
||||
result_block.classList.remove('result-no-output');
|
||||
}
|
||||
})
|
||||
.catch(error => result_block.innerText = 'Playground Communication: ' + error.message);
|
||||
}
|
||||
|
||||
// Syntax highlighting Configuration
|
||||
hljs.configure({
|
||||
tabReplace: ' ', // 4 spaces
|
||||
languages: [], // Languages used for auto-detection
|
||||
});
|
||||
|
||||
const code_nodes = Array
|
||||
.from(document.querySelectorAll('code'))
|
||||
// Don't highlight `inline code` blocks in headers.
|
||||
.filter(function(node) {
|
||||
return !node.parentElement.classList.contains('header');
|
||||
});
|
||||
|
||||
if (window.ace) {
|
||||
// language-rust class needs to be removed for editable
|
||||
// blocks or highlightjs will capture events
|
||||
code_nodes
|
||||
.filter(function(node) {
|
||||
return node.classList.contains('editable');
|
||||
})
|
||||
.forEach(function(block) {
|
||||
block.classList.remove('language-rust');
|
||||
});
|
||||
|
||||
code_nodes
|
||||
.filter(function(node) {
|
||||
return !node.classList.contains('editable');
|
||||
})
|
||||
.forEach(function(block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
} else {
|
||||
code_nodes.forEach(function(block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
}
|
||||
|
||||
// Adding the hljs class gives code blocks the color css
|
||||
// even if highlighting doesn't apply
|
||||
code_nodes.forEach(function(block) {
|
||||
block.classList.add('hljs');
|
||||
});
|
||||
|
||||
Array.from(document.querySelectorAll('code.hljs')).forEach(function(block) {
|
||||
|
||||
const lines = Array.from(block.querySelectorAll('.boring'));
|
||||
// If no lines were hidden, return
|
||||
if (!lines.length) {
|
||||
return;
|
||||
}
|
||||
block.classList.add('hide-boring');
|
||||
|
||||
const buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
buttons.innerHTML = '<button title="Show hidden lines" \
|
||||
aria-label="Show hidden lines"></button>';
|
||||
buttons.firstChild.innerHTML = document.getElementById('fa-eye').innerHTML;
|
||||
|
||||
// add expand button
|
||||
const pre_block = block.parentNode;
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
|
||||
buttons.firstChild.addEventListener('click', function(e) {
|
||||
if (this.title === 'Show hidden lines') {
|
||||
this.innerHTML = document.getElementById('fa-eye-slash').innerHTML;
|
||||
this.title = 'Hide lines';
|
||||
this.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.remove('hide-boring');
|
||||
} else if (this.title === 'Hide lines') {
|
||||
this.innerHTML = document.getElementById('fa-eye').innerHTML;
|
||||
this.title = 'Show hidden lines';
|
||||
this.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.add('hide-boring');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
Array.from(document.querySelectorAll('pre code')).forEach(function(block) {
|
||||
const pre_block = block.parentNode;
|
||||
if (!pre_block.classList.contains('playground')) {
|
||||
let buttons = pre_block.querySelector('.buttons');
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
const clipButton = document.createElement('button');
|
||||
clipButton.className = 'clip-button';
|
||||
clipButton.title = 'Copy to clipboard';
|
||||
clipButton.setAttribute('aria-label', clipButton.title);
|
||||
clipButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
|
||||
buttons.insertBefore(clipButton, buttons.firstChild);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process playground code blocks
|
||||
Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) {
|
||||
// Add play button
|
||||
let buttons = pre_block.querySelector('.buttons');
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
const runCodeButton = document.createElement('button');
|
||||
runCodeButton.className = 'play-button';
|
||||
runCodeButton.hidden = true;
|
||||
runCodeButton.title = 'Run this code';
|
||||
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||
runCodeButton.innerHTML = document.getElementById('fa-play').innerHTML;
|
||||
|
||||
buttons.insertBefore(runCodeButton, buttons.firstChild);
|
||||
runCodeButton.addEventListener('click', () => {
|
||||
run_rust_code(pre_block);
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
const copyCodeClipboardButton = document.createElement('button');
|
||||
copyCodeClipboardButton.className = 'clip-button';
|
||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||
|
||||
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
||||
}
|
||||
|
||||
const code_block = pre_block.querySelector('code');
|
||||
if (window.ace && code_block.classList.contains('editable')) {
|
||||
const undoChangesButton = document.createElement('button');
|
||||
undoChangesButton.className = 'reset-button';
|
||||
undoChangesButton.title = 'Undo changes';
|
||||
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
|
||||
undoChangesButton.innerHTML +=
|
||||
document.getElementById('fa-clock-rotate-left').innerHTML;
|
||||
|
||||
buttons.insertBefore(undoChangesButton, buttons.firstChild);
|
||||
|
||||
undoChangesButton.addEventListener('click', function() {
|
||||
const editor = window.ace.edit(code_block);
|
||||
editor.setValue(editor.originalCode);
|
||||
editor.clearSelection();
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function themes() {
|
||||
const html = document.querySelector('html');
|
||||
const themeToggleButton = document.getElementById('mdbook-theme-toggle');
|
||||
const themePopup = document.getElementById('mdbook-theme-list');
|
||||
const themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||
const themeIds = [];
|
||||
themePopup.querySelectorAll('button.theme').forEach(function(el) {
|
||||
themeIds.push(el.id);
|
||||
});
|
||||
const stylesheets = {
|
||||
ayuHighlight: document.querySelector('#mdbook-ayu-highlight-css'),
|
||||
tomorrowNight: document.querySelector('#mdbook-tomorrow-night-css'),
|
||||
highlight: document.querySelector('#mdbook-highlight-css'),
|
||||
};
|
||||
|
||||
function showThemes() {
|
||||
themePopup.style.display = 'block';
|
||||
themeToggleButton.setAttribute('aria-expanded', true);
|
||||
themePopup.querySelector('button#mdbook-theme-' + get_theme()).focus();
|
||||
}
|
||||
|
||||
function updateThemeSelected() {
|
||||
themePopup.querySelectorAll('.theme-selected').forEach(function(el) {
|
||||
el.classList.remove('theme-selected');
|
||||
});
|
||||
const selected = get_saved_theme() ?? 'default_theme';
|
||||
let element = themePopup.querySelector('button#mdbook-theme-' + selected);
|
||||
if (element === null) {
|
||||
// Fall back in case there is no "Default" item.
|
||||
element = themePopup.querySelector('button#mdbook-theme-' + get_theme());
|
||||
}
|
||||
element.classList.add('theme-selected');
|
||||
}
|
||||
|
||||
function hideThemes() {
|
||||
themePopup.style.display = 'none';
|
||||
themeToggleButton.setAttribute('aria-expanded', false);
|
||||
themeToggleButton.focus();
|
||||
}
|
||||
|
||||
function get_saved_theme() {
|
||||
let theme = null;
|
||||
try {
|
||||
theme = localStorage.getItem('mdbook-theme');
|
||||
} catch {
|
||||
// ignore error.
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
|
||||
function delete_saved_theme() {
|
||||
localStorage.removeItem('mdbook-theme');
|
||||
}
|
||||
|
||||
function get_theme() {
|
||||
const theme = get_saved_theme();
|
||||
if (theme === null || theme === undefined || !themeIds.includes('mdbook-theme-' + theme)) {
|
||||
if (typeof default_dark_theme === 'undefined') {
|
||||
// A customized index.hbs might not define this, so fall back to
|
||||
// old behavior of determining the default on page load.
|
||||
return default_theme;
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? default_dark_theme
|
||||
: default_light_theme;
|
||||
} else {
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
|
||||
let previousTheme = default_theme;
|
||||
function set_theme(theme, store = true) {
|
||||
let ace_theme;
|
||||
|
||||
if (theme === 'coal' || theme === 'navy') {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = false;
|
||||
stylesheets.highlight.disabled = true;
|
||||
|
||||
ace_theme = 'ace/theme/tomorrow_night';
|
||||
} else if (theme === 'ayu') {
|
||||
stylesheets.ayuHighlight.disabled = false;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = true;
|
||||
ace_theme = 'ace/theme/tomorrow_night';
|
||||
} else {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = false;
|
||||
ace_theme = 'ace/theme/dawn';
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor;
|
||||
}, 1);
|
||||
|
||||
if (window.ace && window.editors) {
|
||||
window.editors.forEach(function(editor) {
|
||||
editor.setTheme(ace_theme);
|
||||
});
|
||||
}
|
||||
|
||||
if (store) {
|
||||
try {
|
||||
localStorage.setItem('mdbook-theme', theme);
|
||||
} catch {
|
||||
// ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
html.classList.remove(previousTheme);
|
||||
html.classList.add(theme);
|
||||
previousTheme = theme;
|
||||
updateThemeSelected();
|
||||
}
|
||||
|
||||
const query = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
query.onchange = function() {
|
||||
set_theme(get_theme(), false);
|
||||
};
|
||||
|
||||
// Set theme.
|
||||
set_theme(get_theme(), false);
|
||||
|
||||
themeToggleButton.addEventListener('click', function() {
|
||||
if (themePopup.style.display === 'block') {
|
||||
hideThemes();
|
||||
} else {
|
||||
showThemes();
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('click', function(e) {
|
||||
let theme;
|
||||
if (e.target.className === 'theme') {
|
||||
theme = e.target.id;
|
||||
} else if (e.target.parentElement.className === 'theme') {
|
||||
theme = e.target.parentElement.id;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
theme = theme.replace(/^mdbook-theme-/, '');
|
||||
|
||||
if (theme === 'default_theme' || theme === null) {
|
||||
delete_saved_theme();
|
||||
set_theme(get_theme(), false);
|
||||
} else {
|
||||
set_theme(theme);
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('focusout', function(e) {
|
||||
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
|
||||
if (!!e.relatedTarget &&
|
||||
!themeToggleButton.contains(e.relatedTarget) &&
|
||||
!themePopup.contains(e.relatedTarget)
|
||||
) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
// Should not be needed, but it works around an issue on macOS & iOS:
|
||||
// https://github.com/rust-lang/mdBook/issues/628
|
||||
document.addEventListener('click', function(e) {
|
||||
if (themePopup.style.display === 'block' &&
|
||||
!themeToggleButton.contains(e.target) &&
|
||||
!themePopup.contains(e.target)
|
||||
) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
if (!themePopup.contains(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let li;
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
hideThemes();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
li = document.activeElement.parentElement;
|
||||
if (li && li.previousElementSibling) {
|
||||
li.previousElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
li = document.activeElement.parentElement;
|
||||
if (li && li.nextElementSibling) {
|
||||
li.nextElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:first-child button').focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:last-child button').focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function sidebar() {
|
||||
const sidebar = document.getElementById('mdbook-sidebar');
|
||||
const sidebarLinks = document.querySelectorAll('#mdbook-sidebar a');
|
||||
const sidebarToggleButton = document.getElementById('mdbook-sidebar-toggle');
|
||||
const sidebarResizeHandle = document.getElementById('mdbook-sidebar-resize-handle');
|
||||
const sidebarCheckbox = document.getElementById('mdbook-sidebar-toggle-anchor');
|
||||
let firstContact = null;
|
||||
|
||||
|
||||
/* Because we cannot change the `display` using only CSS after/before the transition, we
|
||||
need JS to do it. We change the display to prevent the browsers search to find text inside
|
||||
the collapsed sidebar. */
|
||||
if (!document.documentElement.classList.contains('sidebar-visible')) {
|
||||
sidebar.style.display = 'none';
|
||||
}
|
||||
sidebar.addEventListener('transitionend', () => {
|
||||
/* We only change the display to "none" if we're collapsing the sidebar. */
|
||||
if (!sidebarCheckbox.checked) {
|
||||
sidebar.style.display = 'none';
|
||||
}
|
||||
});
|
||||
sidebarToggleButton.addEventListener('click', () => {
|
||||
/* To allow the sidebar expansion animation, we first need to put back the display. */
|
||||
if (!sidebarCheckbox.checked) {
|
||||
sidebar.style.display = '';
|
||||
// Workaround for Safari skipping the animation when changing
|
||||
// `display` and a transform in the same event loop. This forces a
|
||||
// reflow after updating the display.
|
||||
sidebar.offsetHeight;
|
||||
}
|
||||
});
|
||||
|
||||
function showSidebar() {
|
||||
document.documentElement.classList.add('sidebar-visible');
|
||||
Array.from(sidebarLinks).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', 0);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', true);
|
||||
sidebar.setAttribute('aria-hidden', false);
|
||||
try {
|
||||
localStorage.setItem('mdbook-sidebar', 'visible');
|
||||
} catch {
|
||||
// Ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
function hideSidebar() {
|
||||
document.documentElement.classList.remove('sidebar-visible');
|
||||
Array.from(sidebarLinks).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', -1);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', false);
|
||||
sidebar.setAttribute('aria-hidden', true);
|
||||
try {
|
||||
localStorage.setItem('mdbook-sidebar', 'hidden');
|
||||
} catch {
|
||||
// Ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle sidebar
|
||||
sidebarCheckbox.addEventListener('change', function sidebarToggle() {
|
||||
if (sidebarCheckbox.checked) {
|
||||
const current_width = parseInt(
|
||||
document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10);
|
||||
if (current_width < 150) {
|
||||
document.documentElement.style.setProperty('--sidebar-target-width', '150px');
|
||||
}
|
||||
showSidebar();
|
||||
} else {
|
||||
hideSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
|
||||
|
||||
function initResize() {
|
||||
window.addEventListener('mousemove', resize, false);
|
||||
window.addEventListener('mouseup', stopResize, false);
|
||||
document.documentElement.classList.add('sidebar-resizing');
|
||||
}
|
||||
function resize(e) {
|
||||
let pos = e.clientX - sidebar.offsetLeft;
|
||||
if (pos < 20) {
|
||||
hideSidebar();
|
||||
} else {
|
||||
if (!document.documentElement.classList.contains('sidebar-visible')) {
|
||||
showSidebar();
|
||||
}
|
||||
pos = Math.min(pos, window.innerWidth - 100);
|
||||
document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px');
|
||||
}
|
||||
}
|
||||
//on mouseup remove windows functions mousemove & mouseup
|
||||
function stopResize() {
|
||||
document.documentElement.classList.remove('sidebar-resizing');
|
||||
window.removeEventListener('mousemove', resize, false);
|
||||
window.removeEventListener('mouseup', stopResize, false);
|
||||
}
|
||||
|
||||
document.addEventListener('touchstart', function(e) {
|
||||
firstContact = {
|
||||
x: e.touches[0].clientX,
|
||||
time: Date.now(),
|
||||
};
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchmove', function(e) {
|
||||
if (!firstContact) {
|
||||
return;
|
||||
}
|
||||
|
||||
const curX = e.touches[0].clientX;
|
||||
const xDiff = curX - firstContact.x,
|
||||
tDiff = Date.now() - firstContact.time;
|
||||
|
||||
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
|
||||
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) {
|
||||
showSidebar();
|
||||
} else if (xDiff < 0 && curX < 300) {
|
||||
hideSidebar();
|
||||
}
|
||||
|
||||
firstContact = null;
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
(function chapterNavigation() {
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey) {
|
||||
return;
|
||||
}
|
||||
if (window.search && window.search.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
const html = document.querySelector('html');
|
||||
|
||||
function next() {
|
||||
const nextButton = document.querySelector('.nav-chapters.next');
|
||||
if (nextButton) {
|
||||
window.location.href = nextButton.href;
|
||||
}
|
||||
}
|
||||
function prev() {
|
||||
const previousButton = document.querySelector('.nav-chapters.previous');
|
||||
if (previousButton) {
|
||||
window.location.href = previousButton.href;
|
||||
}
|
||||
}
|
||||
function showHelp() {
|
||||
const container = document.getElementById('mdbook-help-container');
|
||||
const overlay = document.getElementById('mdbook-help-popup');
|
||||
container.style.display = 'flex';
|
||||
|
||||
// Clicking outside the popup will dismiss it.
|
||||
const mouseHandler = event => {
|
||||
if (overlay.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.removeEventListener('mousedown', mouseHandler);
|
||||
hideHelp();
|
||||
};
|
||||
|
||||
// Pressing esc will dismiss the popup.
|
||||
const escapeKeyHandler = event => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.removeEventListener('keydown', escapeKeyHandler, true);
|
||||
hideHelp();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', escapeKeyHandler, true);
|
||||
document.getElementById('mdbook-help-container')
|
||||
.addEventListener('mousedown', mouseHandler);
|
||||
}
|
||||
function hideHelp() {
|
||||
document.getElementById('mdbook-help-container').style.display = 'none';
|
||||
}
|
||||
|
||||
// Usually needs the Shift key to be pressed
|
||||
switch (e.key) {
|
||||
case '?':
|
||||
e.preventDefault();
|
||||
showHelp();
|
||||
break;
|
||||
}
|
||||
|
||||
// Rest of the keys are only active when the Shift key is not pressed
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
e.preventDefault();
|
||||
if (html.dir === 'rtl') {
|
||||
prev();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
if (html.dir === 'rtl') {
|
||||
next();
|
||||
} else {
|
||||
prev();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function clipboard() {
|
||||
const clipButtons = document.querySelectorAll('.clip-button');
|
||||
|
||||
function hideTooltip(elem) {
|
||||
elem.firstChild.innerText = '';
|
||||
elem.className = 'clip-button';
|
||||
}
|
||||
|
||||
function showTooltip(elem, msg) {
|
||||
elem.firstChild.innerText = msg;
|
||||
elem.className = 'clip-button tooltipped';
|
||||
}
|
||||
|
||||
const clipboardSnippets = new ClipboardJS('.clip-button', {
|
||||
text: function(trigger) {
|
||||
hideTooltip(trigger);
|
||||
const playground = trigger.closest('pre');
|
||||
return playground_text(playground, false);
|
||||
},
|
||||
});
|
||||
|
||||
Array.from(clipButtons).forEach(function(clipButton) {
|
||||
clipButton.addEventListener('mouseout', function(e) {
|
||||
hideTooltip(e.currentTarget);
|
||||
});
|
||||
});
|
||||
|
||||
clipboardSnippets.on('success', function(e) {
|
||||
e.clearSelection();
|
||||
showTooltip(e.trigger, 'Copied!');
|
||||
});
|
||||
|
||||
clipboardSnippets.on('error', function(e) {
|
||||
showTooltip(e.trigger, 'Clipboard error!');
|
||||
});
|
||||
})();
|
||||
|
||||
(function scrollToTop() {
|
||||
const menuTitle = document.querySelector('.menu-title');
|
||||
|
||||
menuTitle.addEventListener('click', function() {
|
||||
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
})();
|
||||
|
||||
(function controllMenu() {
|
||||
const menu = document.getElementById('mdbook-menu-bar');
|
||||
|
||||
(function controllPosition() {
|
||||
let scrollTop = document.scrollingElement.scrollTop;
|
||||
let prevScrollTop = scrollTop;
|
||||
const minMenuY = -menu.clientHeight - 50;
|
||||
// When the script loads, the page can be at any scroll (e.g. if you refresh it).
|
||||
menu.style.top = scrollTop + 'px';
|
||||
// Same as parseInt(menu.style.top.slice(0, -2), but faster
|
||||
let topCache = menu.style.top.slice(0, -2);
|
||||
menu.classList.remove('sticky');
|
||||
let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
|
||||
document.addEventListener('scroll', function() {
|
||||
scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
|
||||
// `null` means that it doesn't need to be updated
|
||||
let nextSticky = null;
|
||||
let nextTop = null;
|
||||
const scrollDown = scrollTop > prevScrollTop;
|
||||
const menuPosAbsoluteY = topCache - scrollTop;
|
||||
if (scrollDown) {
|
||||
nextSticky = false;
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextTop = prevScrollTop;
|
||||
}
|
||||
} else {
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextSticky = true;
|
||||
} else if (menuPosAbsoluteY < minMenuY) {
|
||||
nextTop = prevScrollTop + minMenuY;
|
||||
}
|
||||
}
|
||||
if (nextSticky === true && stickyCache === false) {
|
||||
menu.classList.add('sticky');
|
||||
stickyCache = true;
|
||||
} else if (nextSticky === false && stickyCache === true) {
|
||||
menu.classList.remove('sticky');
|
||||
stickyCache = false;
|
||||
}
|
||||
if (nextTop !== null) {
|
||||
menu.style.top = nextTop + 'px';
|
||||
topCache = nextTop;
|
||||
}
|
||||
prevScrollTop = scrollTop;
|
||||
}, { passive: true });
|
||||
})();
|
||||
(function controllBorder() {
|
||||
function updateBorder() {
|
||||
if (menu.offsetTop === 0) {
|
||||
menu.classList.remove('bordered');
|
||||
} else {
|
||||
menu.classList.add('bordered');
|
||||
}
|
||||
}
|
||||
updateBorder();
|
||||
document.addEventListener('scroll', updateBorder, { passive: true });
|
||||
})();
|
||||
})();
|
||||
7
userGuide/book/clipboard-1626706a.min.js
vendored
Normal file
7
userGuide/book/clipboard-1626706a.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
756
userGuide/book/css/chrome-ae938929.css
Normal file
756
userGuide/book/css/chrome-ae938929.css
Normal file
@@ -0,0 +1,756 @@
|
||||
/* CSS for UI elements (a.k.a. chrome) */
|
||||
|
||||
html {
|
||||
scrollbar-color: var(--scrollbar) transparent;
|
||||
}
|
||||
#mdbook-searchresults a,
|
||||
.content a:link,
|
||||
a:visited,
|
||||
a > .hljs {
|
||||
color: var(--links);
|
||||
}
|
||||
|
||||
/*
|
||||
mdbook-body-container is necessary because mobile browsers don't seem to like
|
||||
overflow-x on the body tag when there is a <meta name="viewport"> tag.
|
||||
*/
|
||||
#mdbook-body-container {
|
||||
/*
|
||||
This is used when the sidebar pushes the body content off the side of
|
||||
the screen on small screens. Without it, dragging on mobile Safari
|
||||
will want to reposition the viewport in a weird way.
|
||||
*/
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
/* Menu Bar */
|
||||
|
||||
#mdbook-menu-bar,
|
||||
#mdbook-menu-bar-hover-placeholder {
|
||||
z-index: 101;
|
||||
margin: auto calc(0px - var(--page-padding));
|
||||
}
|
||||
#mdbook-menu-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: var(--bg);
|
||||
border-block-end-color: var(--bg);
|
||||
border-block-end-width: 1px;
|
||||
border-block-end-style: solid;
|
||||
}
|
||||
#mdbook-menu-bar.sticky,
|
||||
#mdbook-menu-bar-hover-placeholder:hover + #mdbook-menu-bar,
|
||||
#mdbook-menu-bar:hover,
|
||||
html.sidebar-visible #mdbook-menu-bar {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0 !important;
|
||||
}
|
||||
#mdbook-menu-bar-hover-placeholder {
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: 0;
|
||||
height: var(--menu-bar-height);
|
||||
}
|
||||
#mdbook-menu-bar.bordered {
|
||||
border-block-end-color: var(--table-border-color);
|
||||
}
|
||||
#mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button {
|
||||
position: relative;
|
||||
padding: 0 8px;
|
||||
z-index: 10;
|
||||
line-height: var(--menu-bar-height);
|
||||
cursor: pointer;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
@media only screen and (max-width: 420px) {
|
||||
#mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
.icon-button .fa-svg {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.right-buttons {
|
||||
margin: 0 15px;
|
||||
}
|
||||
.right-buttons a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.left-buttons {
|
||||
display: flex;
|
||||
margin: 0 5px;
|
||||
}
|
||||
html:not(.js) .left-buttons button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
display: inline-block;
|
||||
font-weight: 200;
|
||||
font-size: 2.4rem;
|
||||
line-height: var(--menu-bar-height);
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.menu-title {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-bar,
|
||||
.menu-bar:visited,
|
||||
.nav-chapters,
|
||||
.nav-chapters:visited,
|
||||
.mobile-nav-chapters,
|
||||
.mobile-nav-chapters:visited,
|
||||
.menu-bar .icon-button,
|
||||
.menu-bar a .fa-svg {
|
||||
color: var(--icons);
|
||||
}
|
||||
|
||||
.menu-bar .fa-svg:hover,
|
||||
.menu-bar .icon-button:hover,
|
||||
.nav-chapters:hover,
|
||||
.mobile-nav-chapters .fa-svg:hover {
|
||||
color: var(--icons-hover);
|
||||
}
|
||||
|
||||
/* Nav Icons */
|
||||
|
||||
.nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
max-width: 150px;
|
||||
min-width: 90px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
transition: color 0.5s, background-color 0.5s;
|
||||
}
|
||||
|
||||
.nav-chapters:hover {
|
||||
text-decoration: none;
|
||||
background-color: var(--theme-hover);
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
margin-block-start: 50px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
width: 90px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
/* Only Firefox supports flow-relative values */
|
||||
.previous { float: left; }
|
||||
[dir=rtl] .previous { float: right; }
|
||||
|
||||
/* Only Firefox supports flow-relative values */
|
||||
.next {
|
||||
float: right;
|
||||
right: var(--page-padding);
|
||||
}
|
||||
[dir=rtl] .next {
|
||||
float: left;
|
||||
right: unset;
|
||||
left: var(--page-padding);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1080px) {
|
||||
.nav-wide-wrapper { display: none; }
|
||||
.nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
/* sidebar-visible */
|
||||
@media only screen and (max-width: 1380px) {
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wide-wrapper { display: none; }
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
|
||||
:not(pre) > .hljs {
|
||||
display: inline;
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
:not(pre):not(a) > .hljs {
|
||||
color: var(--inline-code-color);
|
||||
overflow-x: initial;
|
||||
}
|
||||
|
||||
a:hover > .hljs {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
}
|
||||
pre > .buttons {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 0px;
|
||||
top: 2px;
|
||||
margin: 0px;
|
||||
padding: 2px 0px;
|
||||
|
||||
color: var(--sidebar-fg);
|
||||
cursor: pointer;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: visibility 0.1s linear, opacity 0.1s linear;
|
||||
}
|
||||
pre:hover > .buttons {
|
||||
visibility: visible;
|
||||
opacity: 1
|
||||
}
|
||||
pre > .buttons :hover {
|
||||
color: var(--sidebar-active);
|
||||
border-color: var(--icons-hover);
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
pre > .buttons button {
|
||||
cursor: inherit;
|
||||
margin: 0px 5px;
|
||||
padding: 2px 3px 0px 4px;
|
||||
font-size: 23px;
|
||||
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 4px;
|
||||
border-color: var(--icons);
|
||||
background-color: var(--theme-popup-bg);
|
||||
transition: 100ms;
|
||||
transition-property: color,border-color,background-color;
|
||||
color: var(--icons);
|
||||
}
|
||||
|
||||
pre > .buttons button.clip-button {
|
||||
padding: 2px 4px 0px 6px;
|
||||
}
|
||||
pre > .buttons button.clip-button::before {
|
||||
/* clipboard image from octicons (https://github.com/primer/octicons/tree/v2.0.0) MIT license
|
||||
*/
|
||||
content: url('data:image/svg+xml,<svg width="21" height="20" viewBox="0 0 24 25" \
|
||||
xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard">\
|
||||
<path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 \
|
||||
0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 \
|
||||
7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 \
|
||||
2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/>\
|
||||
<path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/>\
|
||||
</svg>');
|
||||
filter: var(--copy-button-filter);
|
||||
}
|
||||
pre > .buttons button.clip-button:hover::before {
|
||||
filter: var(--copy-button-filter-hover);
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
pre > .buttons button {
|
||||
/* On mobile, make it easier to tap buttons. */
|
||||
padding: 0.3rem 1rem;
|
||||
}
|
||||
|
||||
.sidebar-resize-indicator {
|
||||
/* Hide resize indicator on devices with limited accuracy */
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
pre > code {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* FIXME: ACE editors overlap their buttons because ACE does absolute
|
||||
positioning within the code block which breaks padding. The only solution I
|
||||
can think of is to move the padding to the outer pre tag (or insert a div
|
||||
wrapper), but that would require fixing a whole bunch of CSS rules.
|
||||
*/
|
||||
.hljs.ace_editor {
|
||||
padding: 0rem 0rem;
|
||||
}
|
||||
|
||||
pre > .result {
|
||||
margin-block-start: 10px;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
|
||||
#mdbook-searchresults a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
mark {
|
||||
border-radius: 2px;
|
||||
padding-block-start: 0;
|
||||
padding-block-end: 1px;
|
||||
padding-inline-start: 3px;
|
||||
padding-inline-end: 3px;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: -1px;
|
||||
margin-inline-start: -3px;
|
||||
margin-inline-end: -3px;
|
||||
background-color: var(--search-mark-bg);
|
||||
transition: background-color 300ms linear;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
mark.fade-out {
|
||||
background-color: rgba(0,0,0,0) !important;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.searchbar-outer {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
|
||||
#mdbook-searchbar-outer.searching #mdbook-searchbar {
|
||||
padding-right: 30px;
|
||||
}
|
||||
#mdbook-searchbar-outer .spinner-wrapper {
|
||||
display: none;
|
||||
}
|
||||
#mdbook-searchbar-outer.searching .spinner-wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.spinner-wrapper {
|
||||
--spinner-margin: 2px;
|
||||
position: absolute;
|
||||
margin-block-start: calc(var(--searchbar-margin-block-start) + var(--spinner-margin));
|
||||
right: var(--spinner-margin);
|
||||
top: 0;
|
||||
bottom: var(--spinner-margin);
|
||||
padding: 6px;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
#fa-spin {
|
||||
animation: rotating 2s linear infinite;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#mdbook-searchbar {
|
||||
width: 100%;
|
||||
margin-block-start: var(--searchbar-margin-block-start);
|
||||
margin-block-end: 0;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
padding: 10px 16px;
|
||||
transition: box-shadow 300ms ease-in-out;
|
||||
border: 1px solid var(--searchbar-border-color);
|
||||
border-radius: 3px;
|
||||
background-color: var(--searchbar-bg);
|
||||
color: var(--searchbar-fg);
|
||||
}
|
||||
#mdbook-searchbar:focus,
|
||||
#mdbook-searchbar.active {
|
||||
box-shadow: 0 0 3px var(--searchbar-shadow-color);
|
||||
}
|
||||
|
||||
.searchresults-header {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding-block-start: 18px;
|
||||
padding-block-end: 0;
|
||||
padding-inline-start: 5px;
|
||||
padding-inline-end: 0;
|
||||
color: var(--searchresults-header-fg);
|
||||
}
|
||||
|
||||
.searchresults-outer {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
border-block-end: 1px dashed var(--searchresults-border-color);
|
||||
}
|
||||
|
||||
ul#mdbook-searchresults {
|
||||
list-style: none;
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
ul#mdbook-searchresults li {
|
||||
margin: 10px 0px;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
ul#mdbook-searchresults li.focus {
|
||||
background-color: var(--searchresults-li-bg);
|
||||
}
|
||||
ul#mdbook-searchresults span.teaser {
|
||||
display: block;
|
||||
clear: both;
|
||||
margin-block-start: 5px;
|
||||
margin-block-end: 0;
|
||||
margin-inline-start: 20px;
|
||||
margin-inline-end: 0;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
ul#mdbook-searchresults span.teaser em {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--sidebar-width);
|
||||
font-size: 0.875em;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-y: contain;
|
||||
background-color: var(--sidebar-bg);
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
.sidebar-iframe-inner {
|
||||
--padding: 10px;
|
||||
|
||||
background-color: var(--sidebar-bg);
|
||||
padding: var(--padding);
|
||||
margin: 0;
|
||||
font-size: 1.4rem;
|
||||
color: var(--sidebar-fg);
|
||||
min-height: calc(100vh - var(--padding) * 2);
|
||||
}
|
||||
.sidebar-iframe-outer {
|
||||
border: none;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
[dir=rtl] .sidebar { left: unset; right: 0; }
|
||||
.sidebar-resizing {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
html:not(.sidebar-resizing) .sidebar {
|
||||
transition: transform 0.3s; /* Animation: slide away */
|
||||
}
|
||||
.sidebar code {
|
||||
line-height: 2em;
|
||||
}
|
||||
.sidebar .sidebar-scrollbox {
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
.sidebar .sidebar-resize-handle {
|
||||
position: absolute;
|
||||
cursor: col-resize;
|
||||
width: 0;
|
||||
right: calc(var(--sidebar-resize-indicator-width) * -1);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-resize-handle .sidebar-resize-indicator {
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
color: var(--icons);
|
||||
margin-inline-start: var(--sidebar-resize-indicator-space);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.sidebar-resize-handle .sidebar-resize-indicator::before {
|
||||
content: "";
|
||||
width: 2px;
|
||||
height: 12px;
|
||||
border-left: dotted 2px currentColor;
|
||||
}
|
||||
.sidebar-resize-handle .sidebar-resize-indicator::after {
|
||||
content: "";
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
border-left: dotted 2px currentColor;
|
||||
}
|
||||
|
||||
[dir=rtl] .sidebar .sidebar-resize-handle {
|
||||
left: calc(var(--sidebar-resize-indicator-width) * -1);
|
||||
right: unset;
|
||||
}
|
||||
.js .sidebar .sidebar-resize-handle {
|
||||
cursor: col-resize;
|
||||
width: calc(var(--sidebar-resize-indicator-width) - var(--sidebar-resize-indicator-space));
|
||||
}
|
||||
|
||||
html:not(.js) .sidebar-resize-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* sidebar-hidden */
|
||||
#mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar {
|
||||
transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
[dir=rtl] #mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar {
|
||||
transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
.sidebar::-webkit-scrollbar {
|
||||
background: var(--sidebar-bg);
|
||||
}
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar);
|
||||
}
|
||||
|
||||
/* sidebar-visible */
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
[dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
@media only screen and (min-width: 620px) {
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: none;
|
||||
margin-inline-start: calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width));
|
||||
}
|
||||
[dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chapter {
|
||||
list-style: none outside none;
|
||||
padding-inline-start: 0;
|
||||
line-height: 2.2em;
|
||||
}
|
||||
|
||||
.chapter li {
|
||||
color: var(--sidebar-non-existant);
|
||||
}
|
||||
|
||||
/* This is a span wrapping the chapter link and the fold chevron. */
|
||||
.chapter-link-wrapper {
|
||||
/* Used to position the chevron to the right, allowing the text to wrap before it. */
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chapter li a {
|
||||
/* Remove underlines. */
|
||||
text-decoration: none;
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
|
||||
.chapter li a:hover {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.chapter li a.active {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
/* This is the toggle chevron. */
|
||||
.chapter-fold-toggle {
|
||||
cursor: pointer;
|
||||
/* Positions the chevron to the side. */
|
||||
margin-inline-start: auto;
|
||||
padding: 0 10px;
|
||||
user-select: none;
|
||||
opacity: 0.68;
|
||||
}
|
||||
|
||||
.chapter-fold-toggle div {
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
/* collapse the section */
|
||||
.chapter li:not(.expanded) > ol {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chapter li.chapter-item {
|
||||
line-height: 1.5em;
|
||||
margin-block-start: 0.6em;
|
||||
}
|
||||
|
||||
/* When expanded, rotate the chevron to point down. */
|
||||
.chapter li.expanded > span > .chapter-fold-toggle div {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.chapter a.current-header {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.on-this-page {
|
||||
margin-left: 22px;
|
||||
border-inline-start: 4px solid var(--sidebar-header-border-color);
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.on-this-page > ol {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* Horizontal line in chapter list. */
|
||||
.spacer {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.chapter .spacer {
|
||||
background-color: var(--sidebar-spacer);
|
||||
}
|
||||
|
||||
/* On touch devices, add more vertical spacing to make it easier to tap links. */
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||
.chapter li a { padding: 5px 0; }
|
||||
.spacer { margin: 10px 0; }
|
||||
}
|
||||
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
padding-inline-start: 20px;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
/* Theme Menu Popup */
|
||||
|
||||
.theme-popup {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: var(--menu-bar-height);
|
||||
z-index: 1000;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7em;
|
||||
color: var(--fg);
|
||||
background: var(--theme-popup-bg);
|
||||
border: 1px solid var(--theme-popup-border);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: none;
|
||||
/* Don't let the children's background extend past the rounded corners. */
|
||||
overflow: hidden;
|
||||
}
|
||||
[dir=rtl] .theme-popup { left: unset; right: 10px; }
|
||||
.theme-popup .default {
|
||||
color: var(--icons);
|
||||
}
|
||||
.theme-popup .theme {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 2px 20px;
|
||||
line-height: 25px;
|
||||
white-space: nowrap;
|
||||
text-align: start;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
background: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
.theme-popup .theme:hover {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
|
||||
.theme-selected::before {
|
||||
display: inline-block;
|
||||
content: "✓";
|
||||
margin-inline-start: -14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
/* The container for the help popup that covers the whole window. */
|
||||
#mdbook-help-container {
|
||||
/* Position and size for the whole window. */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
/* This uses flex layout (which is set in book.js), and centers the popup
|
||||
in the window.*/
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
/* Dim out the book while the popup is visible. */
|
||||
background: var(--overlay-bg);
|
||||
}
|
||||
|
||||
/* The popup help box. */
|
||||
#mdbook-help-popup {
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.15);
|
||||
min-width: 300px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
border-width: 1px;
|
||||
border-color: var(--theme-popup-border);
|
||||
border-style: solid;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mdbook-help-title {
|
||||
text-align: center;
|
||||
/* mdbook's margin for h2 is way too large. */
|
||||
margin: 10px;
|
||||
}
|
||||
408
userGuide/book/css/general-2459343d.css
Normal file
408
userGuide/book/css/general-2459343d.css
Normal file
@@ -0,0 +1,408 @@
|
||||
/* Base styles and content styles */
|
||||
|
||||
:root {
|
||||
/* Browser default font-size is 16px, this way 1 rem = 10px */
|
||||
font-size: 62.5%;
|
||||
color-scheme: var(--color-scheme);
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
color: var(--fg);
|
||||
background-color: var(--bg);
|
||||
text-size-adjust: none;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 1.6rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--mono-font) !important;
|
||||
font-size: var(--code-font-size);
|
||||
direction: ltr !important;
|
||||
}
|
||||
|
||||
/* make long words/inline code not x overflow */
|
||||
main {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* make wide tables scroll if they overflow */
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* Don't change font size in headers. */
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
font-size: unset;
|
||||
}
|
||||
|
||||
.left { float: left; }
|
||||
.right { float: right; }
|
||||
.boring { opacity: 0.6; }
|
||||
.hide-boring .boring { display: none; }
|
||||
.hidden { display: none !important; }
|
||||
|
||||
h2, h3 { margin-block-start: 2.5em; }
|
||||
h4, h5 { margin-block-start: 2em; }
|
||||
|
||||
.header + .header h3,
|
||||
.header + .header h4,
|
||||
.header + .header h5 {
|
||||
margin-block-start: 1em;
|
||||
}
|
||||
|
||||
h1:target::before,
|
||||
h2:target::before,
|
||||
h3:target::before,
|
||||
h4:target::before,
|
||||
h5:target::before,
|
||||
h6:target::before,
|
||||
dt:target::before {
|
||||
display: inline-block;
|
||||
content: "»";
|
||||
margin-inline-start: -30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
/* This is broken on Safari as of version 14, but is fixed
|
||||
in Safari Technology Preview 117 which I think will be Safari 14.2.
|
||||
https://bugs.webkit.org/show_bug.cgi?id=218076
|
||||
*/
|
||||
:target {
|
||||
/* Safari does not support logical properties */
|
||||
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
|
||||
}
|
||||
|
||||
.page {
|
||||
outline: 0;
|
||||
padding: 0 var(--page-padding);
|
||||
margin-block-start: calc(0px - var(--menu-bar-height)); /* Compensate for the #mdbook-menu-bar-hover-placeholder */
|
||||
}
|
||||
.page-wrapper {
|
||||
box-sizing: border-box;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
html:not(.js) .page-wrapper,
|
||||
.js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
[dir=rtl]:not(.js) .page-wrapper,
|
||||
[dir=rtl].js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-right 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow-y: auto;
|
||||
padding: 0 5px 50px 5px;
|
||||
}
|
||||
.content main {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
.content p { line-height: 1.45em; }
|
||||
.content ol { line-height: 1.45em; }
|
||||
.content ul { line-height: 1.45em; }
|
||||
.content a { text-decoration: none; }
|
||||
.content a:hover { text-decoration: underline; }
|
||||
.content img, .content video { max-width: 100%; }
|
||||
.content .header:link,
|
||||
.content .header:visited {
|
||||
color: var(--fg);
|
||||
}
|
||||
.content .header:link,
|
||||
.content .header:visited:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table td {
|
||||
padding: 3px 20px;
|
||||
border: 1px var(--table-border-color) solid;
|
||||
}
|
||||
table thead {
|
||||
background: var(--table-header-bg);
|
||||
}
|
||||
table thead td {
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
table thead th {
|
||||
padding: 3px 20px;
|
||||
}
|
||||
table thead tr {
|
||||
border: 1px var(--table-header-bg) solid;
|
||||
}
|
||||
/* Alternate background colors for rows */
|
||||
table tbody tr:nth-child(2n) {
|
||||
background: var(--table-alternate-bg);
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
margin: 20px 0;
|
||||
padding: 0 20px;
|
||||
color: var(--fg);
|
||||
background-color: var(--quote-bg);
|
||||
border-block-start: .1em solid var(--quote-border);
|
||||
border-block-end: .1em solid var(--quote-border);
|
||||
}
|
||||
|
||||
/* TODO: Remove .warning in a future version of mdbook, it is replaced by
|
||||
blockquote tags. */
|
||||
.warning {
|
||||
margin: 20px;
|
||||
padding: 0 20px;
|
||||
border-inline-start: 2px solid var(--warning-border);
|
||||
}
|
||||
|
||||
.warning:before {
|
||||
position: absolute;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-inline-start: calc(-1.5rem - 21px);
|
||||
content: "ⓘ";
|
||||
text-align: center;
|
||||
background-color: var(--bg);
|
||||
color: var(--warning-border);
|
||||
font-weight: bold;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
blockquote .warning:before {
|
||||
background-color: var(--quote-bg);
|
||||
}
|
||||
|
||||
kbd {
|
||||
background-color: var(--table-border-color);
|
||||
border-radius: 4px;
|
||||
border: solid 1px var(--theme-popup-border);
|
||||
box-shadow: inset 0 -1px 0 var(--theme-hover);
|
||||
display: inline-block;
|
||||
font-size: var(--code-font-size);
|
||||
font-family: var(--mono-font);
|
||||
line-height: 10px;
|
||||
padding: 4px 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
sup {
|
||||
/* Set the line-height for superscript and footnote references so that there
|
||||
isn't an awkward space appearing above lines that contain the footnote.
|
||||
|
||||
See https://github.com/rust-lang/mdBook/pull/2443#discussion_r1813773583
|
||||
for an explanation.
|
||||
*/
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.footnote-definition {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
/* The default spacing for a list is a little too large. */
|
||||
.footnote-definition ul,
|
||||
.footnote-definition ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.footnote-definition > li {
|
||||
/* Required to position the ::before target */
|
||||
position: relative;
|
||||
}
|
||||
.footnote-definition > li:target {
|
||||
scroll-margin-top: 50vh;
|
||||
}
|
||||
.footnote-reference:target {
|
||||
scroll-margin-top: 50vh;
|
||||
}
|
||||
/* Draws a border around the footnote (including the marker) when it is selected.
|
||||
TODO: If there are multiple linkbacks, highlight which one you just came
|
||||
from so you know which one to click.
|
||||
*/
|
||||
.footnote-definition > li:target::before {
|
||||
border: 2px solid var(--footnote-highlight);
|
||||
border-radius: 6px;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
bottom: -8px;
|
||||
left: -32px;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
}
|
||||
/* Pulses the footnote reference so you can quickly see where you left off reading.
|
||||
This could use some improvement.
|
||||
*/
|
||||
@media not (prefers-reduced-motion) {
|
||||
.footnote-reference:target {
|
||||
animation: fn-highlight 0.8s;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@keyframes fn-highlight {
|
||||
from {
|
||||
background-color: var(--footnote-highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltiptext {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
|
||||
left: -8px; /* Half of the width of the icon */
|
||||
top: -35px;
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 8px;
|
||||
margin: 5px;
|
||||
z-index: 1000;
|
||||
}
|
||||
.tooltipped .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.chapter li.part-title {
|
||||
color: var(--sidebar-fg);
|
||||
margin: 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.result-no-output {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.fa-svg svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: currentColor;
|
||||
margin-bottom: -0.1em;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
/* This uses a CSS counter to add numbers to definitions, but only if there is
|
||||
more than one definition. */
|
||||
dl, dt {
|
||||
counter-reset: dd-counter;
|
||||
}
|
||||
|
||||
/* When there is more than one definition, increment the counter. The first
|
||||
selector selects the first definition, and the second one selects definitions
|
||||
2 and beyond.*/
|
||||
dd:has(+ dd), dd + dd {
|
||||
counter-increment: dd-counter;
|
||||
/* Use flex display to help with positioning the numbers when there is a p
|
||||
tag inside the definition. */
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* Shows the counter for definitions. The first selector selects the first
|
||||
definition, and the second one selections definitions 2 and beyond.*/
|
||||
dd:has(+ dd)::before, dd + dd::before {
|
||||
content: counter(dd-counter) ". ";
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
dd > p {
|
||||
/* For loose definitions that have a p tag inside, don't add a bunch of
|
||||
space before the definition. */
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Remove some excess space from the bottom. */
|
||||
.blockquote-tag p:last-child {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.blockquote-tag {
|
||||
/* Add some padding to make the vertical bar a little taller than the text.*/
|
||||
padding: 2px 0px 2px 20px;
|
||||
/* Add a solid color bar on the left side. */
|
||||
border-inline-start-style: solid;
|
||||
border-inline-start-width: 4px;
|
||||
/* Disable the background color from normal blockquotes . */
|
||||
background-color: inherit;
|
||||
/* Disable border blocks from blockquotes. */
|
||||
border-block-start: none;
|
||||
border-block-end: none;
|
||||
}
|
||||
|
||||
.blockquote-tag-title svg {
|
||||
fill: currentColor;
|
||||
/* Add space between the icon and the title. */
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.blockquote-tag-note {
|
||||
border-inline-start-color: var(--blockquote-note-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-tip {
|
||||
border-inline-start-color: var(--blockquote-tip-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-important {
|
||||
border-inline-start-color: var(--blockquote-important-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-warning {
|
||||
border-inline-start-color: var(--blockquote-warning-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-caution {
|
||||
border-inline-start-color: var(--blockquote-caution-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-note .blockquote-tag-title {
|
||||
color: var(--blockquote-note-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-tip .blockquote-tag-title {
|
||||
color: var(--blockquote-tip-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-important .blockquote-tag-title {
|
||||
color: var(--blockquote-important-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-warning .blockquote-tag-title {
|
||||
color: var(--blockquote-warning-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-caution .blockquote-tag-title {
|
||||
color: var(--blockquote-caution-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-title {
|
||||
/* Slightly increase the weight for more emphasis. */
|
||||
font-weight: 600;
|
||||
/* Vertically center the icon with the text. */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* Remove default large margins for a more compact display. */
|
||||
margin: 2px 0 8px 0;
|
||||
}
|
||||
|
||||
.blockquote-tag-title .fa-svg {
|
||||
fill: currentColor;
|
||||
/* Add some space between the icon and the text. */
|
||||
margin-right: 8px;
|
||||
}
|
||||
50
userGuide/book/css/print-9e4910d8.css
Normal file
50
userGuide/book/css/print-9e4910d8.css
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
#mdbook-sidebar,
|
||||
#mdbook-menu-bar,
|
||||
.nav-chapters,
|
||||
.mobile-nav-chapters {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mdbook-page-wrapper.page-wrapper {
|
||||
transform: none !important;
|
||||
margin-inline-start: 0px;
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
#mdbook-content {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page {
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
code {
|
||||
direction: ltr !important;
|
||||
}
|
||||
|
||||
pre > .buttons {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
a, a:visited, a:active, a:hover {
|
||||
color: #4183c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
page-break-inside: avoid;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
page-break-inside: avoid;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.fa {
|
||||
display: none !important;
|
||||
}
|
||||
383
userGuide/book/css/variables-8adf115d.css
Normal file
383
userGuide/book/css/variables-8adf115d.css
Normal file
@@ -0,0 +1,383 @@
|
||||
|
||||
/* Globals */
|
||||
|
||||
:root {
|
||||
--sidebar-target-width: 300px;
|
||||
--sidebar-width: min(var(--sidebar-target-width), 80vw);
|
||||
--sidebar-resize-indicator-width: 8px;
|
||||
--sidebar-resize-indicator-space: 2px;
|
||||
--page-padding: 15px;
|
||||
--content-max-width: 750px;
|
||||
--menu-bar-height: 50px;
|
||||
--mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
|
||||
--code-font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
|
||||
--searchbar-margin-block-start: 5px;
|
||||
}
|
||||
|
||||
/* Themes */
|
||||
|
||||
.ayu {
|
||||
--bg: hsl(210, 25%, 8%);
|
||||
--fg: #c5c5c5;
|
||||
|
||||
--sidebar-bg: #14191f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #5c6773;
|
||||
--sidebar-active: #ffb454;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #0096cf;
|
||||
|
||||
--inline-code-color: #ffb454;
|
||||
|
||||
--theme-popup-bg: #14191f;
|
||||
--theme-popup-border: #5c6773;
|
||||
--theme-hover: #191f26;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(210, 25%, 13%);
|
||||
--table-header-bg: hsl(210, 25%, 28%);
|
||||
--table-alternate-bg: hsl(210, 25%, 11%);
|
||||
|
||||
--searchbar-border-color: #848484;
|
||||
--searchbar-bg: #424242;
|
||||
--searchbar-fg: #fff;
|
||||
--searchbar-shadow-color: #d4c89f;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #252932;
|
||||
--search-mark-bg: #e3b171;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(45%) sepia(6%) saturate(621%) hue-rotate(198deg) brightness(99%) contrast(85%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(68%) sepia(55%) saturate(531%) hue-rotate(341deg) brightness(104%) contrast(101%);
|
||||
|
||||
--footnote-highlight: #2668a6;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #74b9ff;
|
||||
--blockquote-tip-color: #09ca09;
|
||||
--blockquote-important-color: #d3abff;
|
||||
--blockquote-warning-color: #f0b72f;
|
||||
--blockquote-caution-color: #f21424;
|
||||
|
||||
--sidebar-header-border-color: #c18639;
|
||||
}
|
||||
|
||||
.coal {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);
|
||||
|
||||
--footnote-highlight: #4079ae;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #4493f8;
|
||||
--blockquote-tip-color: #08ae08;
|
||||
--blockquote-important-color: #ab7df8;
|
||||
--blockquote-warning-color: #d29922;
|
||||
--blockquote-caution-color: #d91b29;
|
||||
|
||||
--sidebar-header-border-color: #3473ad;
|
||||
}
|
||||
|
||||
.light, html:not(.js) {
|
||||
--bg: hsl(0, 0%, 100%);
|
||||
--fg: hsl(0, 0%, 0%);
|
||||
|
||||
--sidebar-bg: #fafafa;
|
||||
--sidebar-fg: hsl(0, 0%, 0%);
|
||||
--sidebar-non-existant: #aaaaaa;
|
||||
--sidebar-active: #1f1fff;
|
||||
--sidebar-spacer: #f4f4f4;
|
||||
|
||||
--scrollbar: #8F8F8F;
|
||||
|
||||
--icons: #747474;
|
||||
--icons-hover: #000000;
|
||||
|
||||
--links: #20609f;
|
||||
|
||||
--inline-code-color: #301900;
|
||||
|
||||
--theme-popup-bg: #fafafa;
|
||||
--theme-popup-border: #cccccc;
|
||||
--theme-hover: #e6e6e6;
|
||||
|
||||
--quote-bg: hsl(197, 37%, 96%);
|
||||
--quote-border: hsl(197, 37%, 91%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(0, 0%, 95%);
|
||||
--table-header-bg: hsl(0, 0%, 80%);
|
||||
--table-alternate-bg: hsl(0, 0%, 97%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #e4f2fe;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--color-scheme: light;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(45.49%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(14%) sepia(93%) saturate(4250%) hue-rotate(243deg) brightness(99%) contrast(130%);
|
||||
|
||||
--footnote-highlight: #7e7eff;
|
||||
|
||||
--overlay-bg: rgba(200, 200, 205, 0.4);
|
||||
|
||||
--blockquote-note-color: #0969da;
|
||||
--blockquote-tip-color: #008000;
|
||||
--blockquote-important-color: #8250df;
|
||||
--blockquote-warning-color: #9a6700;
|
||||
--blockquote-caution-color: #b52731;
|
||||
|
||||
--sidebar-header-border-color: #6e6edb;
|
||||
}
|
||||
|
||||
.navy {
|
||||
--bg: hsl(226, 23%, 11%);
|
||||
--fg: #bcbdd0;
|
||||
|
||||
--sidebar-bg: #282d3f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505274;
|
||||
--sidebar-active: #2b79a2;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: #282e40;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(226, 23%, 16%);
|
||||
--table-header-bg: hsl(226, 23%, 31%);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%);
|
||||
|
||||
--footnote-highlight: #4079ae;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #4493f8;
|
||||
--blockquote-tip-color: #09ca09;
|
||||
--blockquote-important-color: #ab7df8;
|
||||
--blockquote-warning-color: #d29922;
|
||||
--blockquote-caution-color: #f21424;
|
||||
|
||||
--sidebar-header-border-color: #2f6ab5;
|
||||
}
|
||||
|
||||
.rust {
|
||||
--bg: hsl(60, 9%, 87%);
|
||||
--fg: #262625;
|
||||
|
||||
--sidebar-bg: #3b2e2a;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #e69f67;
|
||||
--sidebar-spacer: #45373a;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #262625;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #6e6b5e;
|
||||
|
||||
--theme-popup-bg: #e1e1db;
|
||||
--theme-popup-border: #b38f6b;
|
||||
--theme-hover: #99908a;
|
||||
|
||||
--quote-bg: hsl(60, 5%, 75%);
|
||||
--quote-border: hsl(60, 5%, 70%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(60, 9%, 82%);
|
||||
--table-header-bg: #b3a497;
|
||||
--table-alternate-bg: hsl(60, 9%, 84%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #dec2a2;
|
||||
--search-mark-bg: #e69f67;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(77%) sepia(16%) saturate(1798%) hue-rotate(328deg) brightness(98%) contrast(83%);
|
||||
|
||||
--footnote-highlight: #d3a17a;
|
||||
|
||||
--overlay-bg: rgba(150, 150, 150, 0.25);
|
||||
|
||||
--blockquote-note-color: #023b95;
|
||||
--blockquote-tip-color: #007700;
|
||||
--blockquote-important-color: #8250df;
|
||||
--blockquote-warning-color: #603700;
|
||||
--blockquote-caution-color: #aa1721;
|
||||
|
||||
--sidebar-header-border-color: #8c391f;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not(.js) {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);
|
||||
|
||||
--footnote-highlight: #4079ae;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #4493f8;
|
||||
--blockquote-tip-color: #08ae08;
|
||||
--blockquote-important-color: #ab7df8;
|
||||
--blockquote-warning-color: #d29922;
|
||||
--blockquote-caution-color: #d91b29;
|
||||
|
||||
--sidebar-header-border-color: #3473ad;
|
||||
}
|
||||
}
|
||||
10
userGuide/book/elasticlunr-ef4e11c1.min.js
vendored
Normal file
10
userGuide/book/elasticlunr-ef4e11c1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
userGuide/book/favicon-8114d1fc.png
Normal file
BIN
userGuide/book/favicon-8114d1fc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
22
userGuide/book/favicon-de23e50b.svg
Normal file
22
userGuide/book/favicon-de23e50b.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 199.7 184.2">
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
svg { fill: white; }
|
||||
}
|
||||
</style>
|
||||
<path d="M189.5,36.8c0.2,2.8,0,5.1-0.6,6.8L153,162c-0.6,2.1-2,3.7-4.2,5c-2.2,1.2-4.4,1.9-6.7,1.9H31.4c-9.6,0-15.3-2.8-17.3-8.4
|
||||
c-0.8-2.2-0.8-3.9,0.1-5.2c0.9-1.2,2.4-1.8,4.6-1.8H123c7.4,0,12.6-1.4,15.4-4.1s5.7-8.9,8.6-18.4l32.9-108.6
|
||||
c1.8-5.9,1-11.1-2.2-15.6S169.9,0,164,0H72.7c-1,0-3.1,0.4-6.1,1.1l0.1-0.4C64.5,0.2,62.6,0,61,0.1s-3,0.5-4.3,1.4
|
||||
c-1.3,0.9-2.4,1.8-3.2,2.8S52,6.5,51.2,8.1c-0.8,1.6-1.4,3-1.9,4.3s-1.1,2.7-1.8,4.2c-0.7,1.5-1.3,2.7-2,3.7c-0.5,0.6-1.2,1.5-2,2.5
|
||||
s-1.6,2-2.2,2.8s-0.9,1.5-1.1,2.2c-0.2,0.7-0.1,1.8,0.2,3.2c0.3,1.4,0.4,2.4,0.4,3.1c-0.3,3-1.4,6.9-3.3,11.6
|
||||
c-1.9,4.7-3.6,8.1-5.1,10.1c-0.3,0.4-1.2,1.3-2.6,2.7c-1.4,1.4-2.3,2.6-2.6,3.7c-0.3,0.4-0.3,1.5-0.1,3.4c0.3,1.8,0.4,3.1,0.3,3.8
|
||||
c-0.3,2.7-1.3,6.3-3,10.8c-1.7,4.5-3.4,8.2-5,11c-0.2,0.5-0.9,1.4-2,2.8c-1.1,1.4-1.8,2.5-2,3.4c-0.2,0.6-0.1,1.8,0.1,3.4
|
||||
c0.2,1.6,0.2,2.8-0.1,3.6c-0.6,3-1.8,6.7-3.6,11c-1.8,4.3-3.6,7.9-5.4,11c-0.5,0.8-1.1,1.7-2,2.8c-0.8,1.1-1.5,2-2,2.8
|
||||
s-0.8,1.6-1,2.5c-0.1,0.5,0,1.3,0.4,2.3c0.3,1.1,0.4,1.9,0.4,2.6c-0.1,1.1-0.2,2.6-0.5,4.4c-0.2,1.8-0.4,2.9-0.4,3.2
|
||||
c-1.8,4.8-1.7,9.9,0.2,15.2c2.2,6.2,6.2,11.5,11.9,15.8c5.7,4.3,11.7,6.4,17.8,6.4h110.7c5.2,0,10.1-1.7,14.7-5.2s7.7-7.8,9.2-12.9
|
||||
l33-108.6c1.8-5.8,1-10.9-2.2-15.5C194.9,39.7,192.6,38,189.5,36.8z M59.6,122.8L73.8,80c0,0,7,0,10.8,0s28.8-1.7,25.4,17.5
|
||||
c-3.4,19.2-18.8,25.2-36.8,25.4S59.6,122.8,59.6,122.8z M78.6,116.8c4.7-0.1,18.9-2.9,22.1-17.1S89.2,86.3,89.2,86.3l-8.9,0
|
||||
l-10.2,30.5C70.2,116.9,74,116.9,78.6,116.8z M75.3,68.7L89,26.2h9.8l0.8,34l23.6-34h9.9l-13.6,42.5h-7.1l12.5-35.4l-24.5,35.4h-6.8
|
||||
l-0.8-35L82,68.7H75.3z"/>
|
||||
</svg>
|
||||
<!-- Original image Copyright Dave Gandy — CC BY 4.0 License -->
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
202
userGuide/book/fonts/OPEN-SANS-LICENSE.txt
Normal file
202
userGuide/book/fonts/OPEN-SANS-LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
93
userGuide/book/fonts/SOURCE-CODE-PRO-LICENSE.txt
Normal file
93
userGuide/book/fonts/SOURCE-CODE-PRO-LICENSE.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
100
userGuide/book/fonts/fonts-9644e21d.css
Normal file
100
userGuide/book/fonts/fonts-9644e21d.css
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */
|
||||
|
||||
/* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'),
|
||||
url('../fonts/open-sans-v17-all-charsets-300-7736aa35.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'),
|
||||
url('../fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'),
|
||||
url('../fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'),
|
||||
url('../fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'),
|
||||
url('../fonts/open-sans-v17-all-charsets-600-486c6759.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'),
|
||||
url('../fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'),
|
||||
url('../fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'),
|
||||
url('../fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'),
|
||||
url('../fonts/open-sans-v17-all-charsets-800-3d2c812a.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'),
|
||||
url('../fonts/open-sans-v17-all-charsets-800italic-ba1521ec.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url('../fonts/source-code-pro-v11-all-charsets-500-2bdd9410.woff2') format('woff2');
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
535
userGuide/book/getting_stated/network_customization.html
Normal file
535
userGuide/book/getting_stated/network_customization.html
Normal file
@@ -0,0 +1,535 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Network customization - Eden-sim - User Guide</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon-de23e50b.svg">
|
||||
<link rel="shortcut icon" href="../favicon-8114d1fc.png">
|
||||
<link rel="stylesheet" href="../css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="../css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="../css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="../css/print-9e4910d8.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../fonts/fonts-9644e21d.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="../highlight-493f70e1.css">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="../tomorrow-night-4c0ae647.css">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="../ayu-highlight-3fdfc3ac.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "light";
|
||||
const default_dark_theme = "navy";
|
||||
window.path_to_searchindex_js = "../searchindex-43c2d326.js";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc-81bc4348.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Eden-sim - User Guide</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
<h1 id="network-customization"><a class="header" href="#network-customization">Network customization</a></h1>
|
||||
<h2 id="introduction"><a class="header" href="#introduction">Introduction</a></h2>
|
||||
<p>In the previous chapter, we wrote a simulation script for a simple Ethernet network. In this chapter, we will discuss how to customize it using the attributes of the different classes used.</p>
|
||||
<h2 id="attributes-in-ns-3"><a class="header" href="#attributes-in-ns-3">Attributes in ns-3</a></h2>
|
||||
<p>To organize access and configuration of instantiated object parameters, ns-3 uses its own attribute system.</p>
|
||||
<p>In the previous chapter, we already implemented this overlay using the “SetAttribute()” function to configure the application. This function has two arguments. The first is a string corresponding to the name of the attribute to be changed. The second is the value to be assigned to this attribute.</p>
|
||||
<p>The various attributes available for each object are visible in the C++ implementation of each object. Here is an example of the EthernetChannel object described in contrib/ethernet/model/ethernet-channel.cc.</p>
|
||||
<pre><code class="language-c++">TypeId
|
||||
EthernetChannel::GetTypeId()
|
||||
{
|
||||
static TypeId tid =
|
||||
TypeId("ns3::EthernetChannel")
|
||||
.SetParent<Channel>()
|
||||
.SetGroupName("Ethernet")
|
||||
.AddConstructor<EthernetChannel>()
|
||||
.AddAttribute("Delay",
|
||||
"Propagation delay through the channel",
|
||||
TimeValue(NanoSeconds(25)),
|
||||
MakeTimeAccessor(&EthernetChannel::m_delay),
|
||||
MakeTimeChecker())
|
||||
.AddTraceSource("TxRxEthernet",
|
||||
"Trace source indicating transmission of packet "
|
||||
"from the EthernetChannel, used by the Animation "
|
||||
"interface.",
|
||||
MakeTraceSourceAccessor(&EthernetChannel::m_txrxEthernet),
|
||||
"ns3::EthernetChannel::TxRxAnimationCallback");
|
||||
return tid;
|
||||
}
|
||||
</code></pre>
|
||||
<p>We can see that this object has only one attribute called Delay, which allows you to configure the propagation delay. It is of type Time and has a default value of 25ns.</p>
|
||||
<p>Thus, it is possible to configure the propagation delay in the previous simulation script as follows:</p>
|
||||
<pre><code class="language-c++"> [...]
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
[...]
|
||||
</code></pre>
|
||||
<h2 id="common-attributes"><a class="header" href="#common-attributes">Common attributes</a></h2>
|
||||
<p>Here are examples of the attributes most often used in Ethernet network simulations.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
net2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
swnet0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
swnet1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
swnet2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
[...]
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
[...]
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(2)));
|
||||
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(5)));
|
||||
n3->AddDevice(sw);
|
||||
|
||||
[...]
|
||||
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
Ptr<DropTailQueue<Packet>> q0 = CreateObject<DropTailQueue<Packet>>();
|
||||
q0->SetAttribute("MaxSize", QueueSizeValue(QueueSize("250p")));
|
||||
net0->SetQueue(q0);
|
||||
|
||||
[...]
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("InterFrame", TimeValue(MilliSeconds(20)));
|
||||
app0->SetAttribute("Jitter", TimeValue(MilliSeconds(50)));
|
||||
app0->SetAttribute("Offset", TimeValue(Seconds(1)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
app0->SetAttribute("DEI", UintegerValue(0));
|
||||
[...]
|
||||
|
||||
</code></pre>
|
||||
<h2 id="final-simulation-script"><a class="header" href="#final-simulation-script">Final simulation script</a></h2>
|
||||
<p>Here is the simulation script you should have at the end of this chapter.</p>
|
||||
<pre><code class="language-c++">#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/ethernet-net-device.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
#include "ns3/ethernet-generator.h"
|
||||
|
||||
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 2");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 2", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<Node> n0 = CreateObject<Node>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<Node> n1 = CreateObject<Node>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<Node> n2 = CreateObject<Node>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<Node> n3 = CreateObject<Node>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
net2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
swnet0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
swnet1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
swnet2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(2)));
|
||||
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(5)));
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet2});
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("InterFrame", TimeValue(MilliSeconds(20)));
|
||||
app0->SetAttribute("Jitter", TimeValue(MilliSeconds(50)));
|
||||
app0->SetAttribute("Offset", TimeValue(Seconds(1)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
app0->SetAttribute("DEI", UintegerValue(0));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../getting_stated/simple_ethernet_network.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../getting_stated/simple_tsn_network.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../getting_stated/simple_ethernet_network.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../getting_stated/simple_tsn_network.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr-ef4e11c1.min.js"></script>
|
||||
<script src="../mark-09e88c2c.min.js"></script>
|
||||
<script src="../searcher-c2a407aa.js"></script>
|
||||
|
||||
<script src="../clipboard-1626706a.min.js"></script>
|
||||
<script src="../highlight-abc7f01d.js"></script>
|
||||
<script src="../book-a0b12cfe.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
607
userGuide/book/getting_stated/simple_ethernet_network.html
Normal file
607
userGuide/book/getting_stated/simple_ethernet_network.html
Normal file
@@ -0,0 +1,607 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Creation of a simple ethernet network - Eden-sim - User Guide</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon-de23e50b.svg">
|
||||
<link rel="shortcut icon" href="../favicon-8114d1fc.png">
|
||||
<link rel="stylesheet" href="../css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="../css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="../css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="../css/print-9e4910d8.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../fonts/fonts-9644e21d.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="../highlight-493f70e1.css">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="../tomorrow-night-4c0ae647.css">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="../ayu-highlight-3fdfc3ac.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "light";
|
||||
const default_dark_theme = "navy";
|
||||
window.path_to_searchindex_js = "../searchindex-43c2d326.js";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc-81bc4348.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Eden-sim - User Guide</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
<h1 id="creation-of-a-simple-ethernet-network"><a class="header" href="#creation-of-a-simple-ethernet-network">Creation of a simple ethernet network</a></h1>
|
||||
<h2 id="introduction"><a class="header" href="#introduction">Introduction</a></h2>
|
||||
<p>To perform a simulation with ns-3, you need to write a C++ program that describes the simulation to be performed. In this chapter, we will create such a simulation script able to simulate an Ethernet network composed of one switch to which three end stations are connected.</p>
|
||||
<h2 id="first-simulation-script"><a class="header" href="#first-simulation-script">First simulation script</a></h2>
|
||||
<p>Let’s start by creating a script that runs a 10-second simulation. To do this, create a file called chapter1.cc in the scratch folder located in the ns-3 folder. In this file, copy the following lines:</p>
|
||||
<pre><code class="language-c++">#include "ns3/simulator.h"
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 1");
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 1", LOG_LEVEL_INFO);
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
</code></pre>
|
||||
<p>To run it, simply execute the following command from the folder where ns-3 is installed.</p>
|
||||
<pre><code class="language-console">./ns3 run scratch/chapter1.cc
|
||||
</code></pre>
|
||||
<p>Congratulations, you have run your first simulation with ns-3! However, this simulation does not simulate anything.</p>
|
||||
<h2 id="topology-description"><a class="header" href="#topology-description">Topology description</a></h2>
|
||||
<p>Before simulating our Ethernet network, we must first describe it. To do this, let’s start by adding the necessary dependencies.</p>
|
||||
<pre><code class="language-c++">#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/ethernet-net-device.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>Next, the topology is described as follows:</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 1", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<Node> n0 = CreateObject<Node>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<Node> n1 = CreateObject<Node>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<Node> n2 = CreateObject<Node>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<Node> n3 = CreateObject<Node>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create two output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>In this code, the following six steps are performed:</p>
|
||||
<ol>
|
||||
<li><em>Creating nodes:</em> In this example, four nodes are created.</li>
|
||||
<li><em>Creating NetDevices:</em> A NetDevice is a network interface in ns-3 terminology. Here, one NetDevice is created and added to each end-station node, and three NetDevices are created and added to the switch node.</li>
|
||||
<li><em>Creating channels:</em> A channel is a link in ns-3 terminology in the case of the wired network considered here. Thus, three channels are created and attached to different NetDevices to connect the three end stations to the switch.</li>
|
||||
<li><em>Creating SwitchNetDevices:</em> The SwitchNetDevice is the object that will perform switching between the switch’s NetDevices. Here, a SwitchNetDevice object is created and attached to the switch node. The switch ports are also attached to it.</li>
|
||||
<li><em>Allocation of Mac addresses:</em> For our example, four mac addresses are allocated using the iterator provided by ns-3.</li>
|
||||
<li><em>Creation of output port queues:</em> For our example, two FIFOs are instantiated per NetDevices.</li>
|
||||
</ol>
|
||||
<p>At this stage, it is possible to run a simulation, but the output will be the same since no traffic is simulated.</p>
|
||||
<h2 id="traffic-simulation"><a class="header" href="#traffic-simulation">Traffic simulation</a></h2>
|
||||
<p>To send Ethernet frames in our network, we need to instantiate applications. To instantiate these applications, we must first add the following dependency.</p>
|
||||
<pre><code class="language-c++">#include “ns3/ethernet-generator.h”
|
||||
</code></pre>
|
||||
<p>This dependency is part of Eden-sim and is used to transmit Q-Tagged Ethernet frames.</p>
|
||||
<p>Next, let’s add an application that sends a burst of two frames with a payload of 1400 bytes, a priority of 1, and a VLAN ID of 1 every 5 seconds. This application is hosted on node ES1. The frames are destined for ES3. Here is the code used to instantiate such an application:</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>With this simulation script complete, you can run a simulation… And you should see nothing new.</p>
|
||||
<p>Indeed, there is no indication for ns-3 to produce any output. So our frames are sent in our simulated network, but we have no output to confirm it. To display logs about our frames in the console, let’s add a callback for the transmission and one for the reception.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 1");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
[...]
|
||||
</code></pre>
|
||||
<pre><code class="language-c++">[...]
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>If you run the simulation again, you should get the following output.</p>
|
||||
<pre><code class="language-console">$ ./ns3 run scratch/book.cc
|
||||
[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+5s ES1:ES1#01 : Pkt #2 sent!
|
||||
+5s ES1:ES1#01 : Pkt #3 sent!
|
||||
End of the simulation
|
||||
</code></pre>
|
||||
<p>We can see that four frames are successfully sent by ES1 but are never received by ES3. This is because, after the first hop, the frames are received by the switch but it does not know which port to send them to. It is therefore necessary to add a static forwarding configuration to our switch.</p>
|
||||
<h2 id="network-configuration"><a class="header" href="#network-configuration">Network configuration</a></h2>
|
||||
<p>Unlike plug-and-play switches, the switches simulated by Eden-sim are designed for critical embedded networks. To guarantee network determinism, these switches do not implement dynamic mechanisms such as mac learning. They must therefore be configured statically before the simulation begins.</p>
|
||||
<p>Here, the only configuration missing to transmit frames to ES3 is the configuration of the static switching table. It is possible to add an entry to this table as follows:</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet2});
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>After this configuration, running the simulation produces the following output, which finally indicates the transmission and reception of frames.</p>
|
||||
<pre><code class="language-console">$ ./ns3 run scratch/book.cc
|
||||
[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+2.293e-05s ES3:ES3#01 : Pkt #0 received!
|
||||
+3.4466e-05s ES3:ES3#01 : Pkt #1 received!
|
||||
+5s ES1:ES1#01 : Pkt #2 sent!
|
||||
+5s ES1:ES1#01 : Pkt #3 sent!
|
||||
+5.00002s ES3:ES3#01 : Pkt #2 received!
|
||||
+5.00003s ES3:ES3#01 : Pkt #3 received!
|
||||
End of the simulation
|
||||
</code></pre>
|
||||
<h2 id="final-simulation-script"><a class="header" href="#final-simulation-script">Final simulation script</a></h2>
|
||||
<p>Here is the simulation script you should have at the end of this chapter.</p>
|
||||
<pre><code class="language-c++">#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/ethernet-net-device.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
#include "ns3/ethernet-generator.h"
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 1");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 1", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<Node> n0 = CreateObject<Node>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<Node> n1 = CreateObject<Node>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<Node> n2 = CreateObject<Node>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<Node> n3 = CreateObject<Node>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet2});
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../prerequisites.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../getting_stated/network_customization.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../prerequisites.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../getting_stated/network_customization.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr-ef4e11c1.min.js"></script>
|
||||
<script src="../mark-09e88c2c.min.js"></script>
|
||||
<script src="../searcher-c2a407aa.js"></script>
|
||||
|
||||
<script src="../clipboard-1626706a.min.js"></script>
|
||||
<script src="../highlight-abc7f01d.js"></script>
|
||||
<script src="../book-a0b12cfe.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
936
userGuide/book/getting_stated/simple_tsn_network.html
Normal file
936
userGuide/book/getting_stated/simple_tsn_network.html
Normal file
@@ -0,0 +1,936 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Creation of a simple tsn network - Eden-sim - User Guide</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon-de23e50b.svg">
|
||||
<link rel="shortcut icon" href="../favicon-8114d1fc.png">
|
||||
<link rel="stylesheet" href="../css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="../css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="../css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="../css/print-9e4910d8.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../fonts/fonts-9644e21d.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="../highlight-493f70e1.css">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="../tomorrow-night-4c0ae647.css">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="../ayu-highlight-3fdfc3ac.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "light";
|
||||
const default_dark_theme = "navy";
|
||||
window.path_to_searchindex_js = "../searchindex-43c2d326.js";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc-81bc4348.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Eden-sim - User Guide</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
<h1 id="creation-of-a-simple-tsn-network"><a class="header" href="#creation-of-a-simple-tsn-network">Creation of a simple tsn network</a></h1>
|
||||
<h2 id="introduction"><a class="header" href="#introduction">Introduction</a></h2>
|
||||
<p>In this chapter, we will explore how to transform the Ethernet network simulation script from the previous chapter into a TSN network simulation script. Then, we will discuss how to instantiate and configure the various TSN mechanisms supported by the simulation library.</p>
|
||||
<p>Note that the operation and configuration of TSN mechanisms will not be explained in depth. This chapter assumes that the reader is familiar with the details of the mechanisms implemented.</p>
|
||||
<h2 id="from-ethernet-to-tsn-simulation"><a class="header" href="#from-ethernet-to-tsn-simulation">From Ethernet to TSN simulation</a></h2>
|
||||
<p>Let’s start by importing two new objects: TsnNode and TsnNetDevice.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
#include "ns3/tsn-node.h"
|
||||
#include "ns3/tsn-net-device.h"
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>Next, it is necessary to replace the node and EthernetNetDevice objects with their TSN versions in order to instantiate the various TSN mechanisms described below.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
//Create four nodes
|
||||
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<TsnNetDevice> swnet2 = CreateObject<TsnNetDevice>();
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>At this stage, it is possible to run a simulation, but the result will be identical to a simple Ethernet network.</p>
|
||||
<p>To simplify the illustration of the latency control mechanisms (CBS and TAS), we can make the following modifications to the application:</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(5));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>The output of a simulation should look like this:</p>
|
||||
<pre><code class="language-console">[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+0s ES1:ES1#01 : Pkt #2 sent!
|
||||
+0s ES1:ES1#01 : Pkt #3 sent!
|
||||
+0s ES1:ES1#01 : Pkt #4 sent!
|
||||
+0.000233905s ES3:ES3#01 : Pkt #0 received!
|
||||
+0.000349265s ES3:ES3#01 : Pkt #1 received!
|
||||
+0.000464625s ES3:ES3#01 : Pkt #2 received!
|
||||
+0.000579985s ES3:ES3#01 : Pkt #3 received!
|
||||
+0.000695345s ES3:ES3#01 : Pkt #4 received!
|
||||
+5s ES1:ES1#01 : Pkt #5 sent!
|
||||
+5s ES1:ES1#01 : Pkt #6 sent!
|
||||
+5s ES1:ES1#01 : Pkt #7 sent!
|
||||
+5s ES1:ES1#01 : Pkt #8 sent!
|
||||
+5s ES1:ES1#01 : Pkt #9 sent!
|
||||
+5.00023s ES3:ES3#01 : Pkt #5 received!
|
||||
+5.00035s ES3:ES3#01 : Pkt #6 received!
|
||||
+5.00046s ES3:ES3#01 : Pkt #7 received!
|
||||
+5.00058s ES3:ES3#01 : Pkt #8 received!
|
||||
+5.0007s ES3:ES3#01 : Pkt #9 received!
|
||||
End of the simulation
|
||||
</code></pre>
|
||||
<h2 id="cbs"><a class="header" href="#cbs">CBS</a></h2>
|
||||
<p>To instantiate a CBS, we must first add its dependency.</p>
|
||||
<pre><code class="language-c++">#include "ns3/cbs.h"
|
||||
</code></pre>
|
||||
<p>It is then possible to instantiate a CBS and link it to an output port queue. In the following example, we instantiate the CBS on the output port of the transmitting end station.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
Ptr<Cbs> cbs = CreateObject<Cbs>();
|
||||
cbs->SetTsnNetDevice(net0);
|
||||
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("20Kb/s")));
|
||||
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs); //FIFO 1
|
||||
|
||||
for (int i=0; i<2; i++){
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>When launching the simulation, we can now see that the reception of burst frames is spread out over time due to the waiting time imposed by the CBS credit reload on the emission port.</p>
|
||||
<pre><code class="language-console">[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+0s ES1:ES1#01 : Pkt #2 sent!
|
||||
+0s ES1:ES1#01 : Pkt #3 sent!
|
||||
+0s ES1:ES1#01 : Pkt #4 sent!
|
||||
+0.000233905s ES3:ES3#01 : Pkt #0 received!
|
||||
+0.577031s ES3:ES3#01 : Pkt #1 received!
|
||||
+1.15383s ES3:ES3#01 : Pkt #2 received!
|
||||
+1.73063s ES3:ES3#01 : Pkt #3 received!
|
||||
+2.30743s ES3:ES3#01 : Pkt #4 received!
|
||||
+5s ES1:ES1#01 : Pkt #5 sent!
|
||||
+5s ES1:ES1#01 : Pkt #6 sent!
|
||||
+5s ES1:ES1#01 : Pkt #7 sent!
|
||||
+5s ES1:ES1#01 : Pkt #8 sent!
|
||||
+5s ES1:ES1#01 : Pkt #9 sent!
|
||||
+5.00023s ES3:ES3#01 : Pkt #5 received!
|
||||
+5.57703s ES3:ES3#01 : Pkt #6 received!
|
||||
+6.15383s ES3:ES3#01 : Pkt #7 received!
|
||||
+6.73063s ES3:ES3#01 : Pkt #8 received!
|
||||
+7.30743s ES3:ES3#01 : Pkt #9 received!
|
||||
End of the simulation
|
||||
</code></pre>
|
||||
<h2 id="tas"><a class="header" href="#tas">TAS</a></h2>
|
||||
<p>To instanciate TAS, we need to add a clock to the TsnNode, add GCL entries to the net device and add 8 FIFOs to the output ports. It can be done as follows:</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
//Add a perfect clock to the SW node
|
||||
n3->AddClock(CreateObject<Clock>());
|
||||
[...]
|
||||
//Create 8 output port FIFOs for each netDevice.
|
||||
Ptr<Cbs> cbs = CreateObject<Cbs>();
|
||||
cbs->SetTsnNetDevice(net0);
|
||||
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("20Kb/s")));
|
||||
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs); //FIFO 1
|
||||
for (int i=0; i<6; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
}
|
||||
|
||||
for (int i=0; i<8; i++){
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
[...]
|
||||
//Configure TAS schedule
|
||||
swnet2->AddGclEntry(Time(Seconds(2)), 0); //All gates are close
|
||||
swnet2->AddGclEntry(Time(Seconds(3)), 2); //Only the gate of the FIFO 1 is open
|
||||
swnet2->StartTas();
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>In this example, a TAS schedule is added to the switch’s output port, used by the flow. This schedule is designed to delay the frames of the two bursts by approximately two seconds compared to the previous example. The result obtained is as follows.</p>
|
||||
<pre><code class="language-console">[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+0s ES1:ES1#01 : Pkt #2 sent!
|
||||
+0s ES1:ES1#01 : Pkt #3 sent!
|
||||
+0s ES1:ES1#01 : Pkt #4 sent!
|
||||
+2.00011s ES3:ES3#01 : Pkt #0 received!
|
||||
+2.00023s ES3:ES3#01 : Pkt #1 received!
|
||||
+2.00035s ES3:ES3#01 : Pkt #2 received!
|
||||
+2.00046s ES3:ES3#01 : Pkt #3 received!
|
||||
+2.30743s ES3:ES3#01 : Pkt #4 received!
|
||||
+5s ES1:ES1#01 : Pkt #5 sent!
|
||||
+5s ES1:ES1#01 : Pkt #6 sent!
|
||||
+5s ES1:ES1#01 : Pkt #7 sent!
|
||||
+5s ES1:ES1#01 : Pkt #8 sent!
|
||||
+5s ES1:ES1#01 : Pkt #9 sent!
|
||||
+7.00011s ES3:ES3#01 : Pkt #5 received!
|
||||
+7.00023s ES3:ES3#01 : Pkt #6 received!
|
||||
+7.00035s ES3:ES3#01 : Pkt #7 received!
|
||||
+7.00046s ES3:ES3#01 : Pkt #8 received!
|
||||
+7.30743s ES3:ES3#01 : Pkt #9 received!
|
||||
End of the simulation
|
||||
</code></pre>
|
||||
<h2 id="gptp"><a class="header" href="#gptp">gPTP</a></h2>
|
||||
<p>To synchronize the nodes in our network using gPTP, several changes must be made. Let’s start with the includes.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
#include "ns3/clock-constant-drift.h"
|
||||
#include "ns3/gPTP.h"
|
||||
#include "ns3/ethernet-header2.h"
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>Next, let’s modify the callbacks that log packet transmission and reception so that events are only logged if the VLAN ID matches the VLAN ID of the flows going from ES1 to ES3. This change prevents the output from being flooded with logs concerning the transmission or reception of synchronization frames. We also add a callback to log the difference between the perfect clock (the simulation time) and the device clock after each correction to verify that gPTP is working properly.</p>
|
||||
<pre><code class="language-c++">//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log clock offset after correction
|
||||
static void
|
||||
ClockAfterCorrectionCallback(std::string context, Time clockValue)
|
||||
{
|
||||
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (Simulator::Now()-clockValue).GetNanoSeconds() << "ns)");
|
||||
}
|
||||
</code></pre>
|
||||
<p>Next, we replace the perfect clock previously instantiated on n3(SW) in the TAS section with clock instantiation on the various network devices. Note that the clock instantiated on ES1(n0) is a perfect clock (i.e., it corresponds to the simulation time) since it acts as the Grandmaster.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add clocks to TsnNodes
|
||||
Ptr<Clock> c0 = CreateObject<Clock>(); //perfect clock because Grandmaster
|
||||
n0->SetMainClock(c0);
|
||||
Ptr<ConstantDriftClock> c1 = CreateObject<ConstantDriftClock>();
|
||||
c1->SetAttribute("InitialOffset", TimeValue(Seconds(20)));
|
||||
c1->SetAttribute("DriftRate", DoubleValue(-50));
|
||||
c1->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n1->SetMainClock(c1);
|
||||
Ptr<ConstantDriftClock> c2 = CreateObject<ConstantDriftClock>();
|
||||
c2->SetAttribute("InitialOffset", TimeValue(Seconds(3)));
|
||||
c2->SetAttribute("DriftRate", DoubleValue(2));
|
||||
c2->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n2->SetMainClock(c2);
|
||||
Ptr<ConstantDriftClock> c3 = CreateObject<ConstantDriftClock>();
|
||||
c3->SetAttribute("InitialOffset", TimeValue(Seconds(0.5)));
|
||||
c3->SetAttribute("DriftRate", DoubleValue(-25));
|
||||
c3->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n3->SetMainClock(c3);
|
||||
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>Then, we add and configure the gPTP instances on the different nodes. Note that the implementation of gPTP in Eden-sim only supports static configuration of gPTP (i.e. no BTCA).</p>
|
||||
<pre><code class="language-c++"> [...]
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
|
||||
gPTP0->SetNode(n0);
|
||||
gPTP0->SetMainClock(c0);
|
||||
gPTP0->AddDomain(0);
|
||||
gPTP0->AddPort(net0, GPTP::MASTER, 0);
|
||||
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
|
||||
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
|
||||
gPTP0->SetAttribute("Priority", UintegerValue(7));
|
||||
n0->AddApplication(gPTP0);
|
||||
gPTP0->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
|
||||
gPTP1->SetNode(n1);
|
||||
gPTP1->SetMainClock(c1);
|
||||
gPTP1->AddDomain(0);
|
||||
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
|
||||
gPTP1->SetAttribute("Priority", UintegerValue(7));
|
||||
n1->AddApplication(gPTP1);
|
||||
gPTP1->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP2 = CreateObject<GPTP>();
|
||||
gPTP2->SetNode(n2);
|
||||
gPTP2->SetMainClock(c2);
|
||||
gPTP2->AddDomain(0);
|
||||
gPTP2->AddPort(net2, GPTP::SLAVE, 0);
|
||||
gPTP2->SetAttribute("Priority", UintegerValue(7));
|
||||
n2->AddApplication(gPTP2);
|
||||
gPTP2->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP3 = CreateObject<GPTP>();
|
||||
gPTP3->SetNode(n3);
|
||||
gPTP3->SetMainClock(c3);
|
||||
gPTP3->AddDomain(0);
|
||||
gPTP3->AddPort(swnet0, GPTP::SLAVE, 0);
|
||||
gPTP3->AddPort(swnet1, GPTP::MASTER, 0);
|
||||
gPTP3->AddPort(swnet2, GPTP::MASTER, 0);
|
||||
gPTP3->SetAttribute("Priority", UintegerValue(7));
|
||||
n3->AddApplication(gPTP3);
|
||||
gPTP3->SetStartTime(Seconds(0));
|
||||
</code></pre>
|
||||
<p>And finally, we add the callbacks.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
//Callback to display clock offset after correction
|
||||
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1)));
|
||||
gPTP2->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n2)));
|
||||
gPTP3->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n3)));
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>When running the simulation, we can observe that gPTP effectively mitigates clock drift thanks to periodic synchronization on SW and ES2. However, we note that there is no gPTP log for ES3. This is due to the TAS configuration, which never opens the FIFO7 gate used by synchronization messages. Of course, such a configuration has no place in a network that is intended to be functional, but in this example it illustrates the impact of the TAS configuration on gPTP packets.</p>
|
||||
<h2 id="stream-identification"><a class="header" href="#stream-identification">Stream Identification</a></h2>
|
||||
<p>Before using PSFP or FRER, it is necessary to implement an identification function. In this section, we will use a null Stream identification function on the switch port connected to ES1. This function will then be used in the following two sections to implement PSFP in order to validate the flow contract and to replicate frames using FRER.</p>
|
||||
<p>Let’s start by importing this function.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
#include "ns3/stream-identification-function-null.h"
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>Next, let’s create and add the identification function on port swnet0 in input and outfacing mode.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
swnet2->StartTas();
|
||||
|
||||
//Add a stream identification function
|
||||
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
|
||||
uint16_t StreamHandle = 10;
|
||||
sif0->SetAttribute("VlanID", UintegerValue(1));
|
||||
sif0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
n3->AddStreamIdentificationFunction(StreamHandle, sif0, {swnet0}, {}, {}, {});
|
||||
[...]
|
||||
</code></pre>
|
||||
<h2 id="psfp"><a class="header" href="#psfp">PSFP</a></h2>
|
||||
<p>Now that we have an identification function capable of identifying frames going from ES1 to ES3 with VLAN ID 1, we can set up a PSFP instance to validate compliance with a network usage contract. Currently, the simulator implements the stream filter and the flow meter. They can be added and configured as follows.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
n3->AddStreamIdentificationFunction(StreamHandle, sif0, {swnet0}, {}, {}, {});
|
||||
|
||||
//PSFP configuration
|
||||
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
|
||||
sfi0->SetAttribute("StreamHandle", IntegerValue(StreamHandle));
|
||||
sfi0->SetAttribute("Priority", IntegerValue(-1)); //-1 = wildcard
|
||||
sfi0->SetAttribute("MaxSDUSize", UintegerValue(1422));
|
||||
n3->AddStreamFilter(sfi0);
|
||||
Ptr<FlowMeterInstance> fm0 = CreateObject<FlowMeterInstance>();
|
||||
fm0->SetAttribute("CIR", DataRateValue(DataRate("20Kb/s")));
|
||||
fm0->SetAttribute("CBS", UintegerValue(1400));
|
||||
fm0->SetAttribute("DropOnYellow", BooleanValue(true));
|
||||
fm0->SetAttribute("MarkAllFramesRedEnable", BooleanValue(false));
|
||||
uint16_t fmid = n3->AddFlowMeter(fm0);
|
||||
sfi0->AddFlowMeterInstanceId(fmid);
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>After these changes, running the simulation does not produce a different output from previous runs. However, by changing the parameters of the emitting application to increase its bandwidth consumption (e.g., increasing the packet size) or by increasing the idle slope of the CBS, it is possible to observe that packets that do not comply with the contract are never received as they are discarded.</p>
|
||||
<h2 id="frer"><a class="header" href="#frer">FRER</a></h2>
|
||||
<p>And finally, let’s instantiate FRER. However, the topology we are working with in this chapter does not have multiple paths between ES1 and ES3. So in this section, we will replicate the frames on the output ports of the switch connected to ES2 and ES3 to illustrate the philosophy behind FRER instantiation. We will only detail the replication part. The elimination part is detailed in the example contrib/tsn/examples/tsn-switched-withFRER.cc, which has a topology much more suited to the use of FRER.</p>
|
||||
<p>Replication with FRER is based on two functions: Sequence Generation Function and Sequence Encode/Decode Function. These two functions are created and configured as follows.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
sfi0->AddFlowMeterInstanceId(fmid);
|
||||
|
||||
//Sequencing : Sequence generation
|
||||
Ptr<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
|
||||
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqf0->SetStreamHandle({StreamHandle});
|
||||
n3->AddSequenceGenerationFunction(seqf0);
|
||||
//Sequence encode
|
||||
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
|
||||
seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqEnc0->SetAttribute("Active", BooleanValue(true));
|
||||
seqEnc0->SetStreamHandle({StreamHandle});
|
||||
seqEnc0->SetPort(swnet0);
|
||||
n3->AddSequenceEncodeDecodeFunction(seqEnc0);
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet1, swnet2});
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>With this configuration, replication is achieved by forwarding to two different ports. This simplifies configuration by not using the FRER splitting function. However, it is necessary to change the configuration of the forwarding table as shown in the previous listing.</p>
|
||||
<p>In order to validate the correct operation of the replication (i.e., the addition of the R-TAG), modify the callbacks as follows to display the frame size at transmission and reception.</p>
|
||||
<pre><code class="language-c++">[...]
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) sent!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) received!");
|
||||
}
|
||||
}
|
||||
[...]
|
||||
</code></pre>
|
||||
<p>And now, when running the simulation script, we observe an increase in frame size of 6 bytes (the size of R-TAG) between transmission and reception, as illustrated below.</p>
|
||||
<pre><code class="language-console">[...]
|
||||
+5s ES1:ES1#01 : Pkt #215(1422bytes) sent!
|
||||
[...]
|
||||
+7.30743s ES3:ES3#01 : Pkt #215(1428bytes) received!
|
||||
[...]
|
||||
</code></pre>
|
||||
<h2 id="conclusion-and-final-simulation-script"><a class="header" href="#conclusion-and-final-simulation-script">Conclusion and final simulation script</a></h2>
|
||||
<p>In this section, we have implemented the various TSN mechanisms of Eden-sim.</p>
|
||||
<p>Note that the examples found in contrib/tsn/examples/ illustrate more complicated configurations and implement different traces to log information about the mechanism’s operation. These examples are a good means of further exploring the uses of these TSN mechanisms.</p>
|
||||
<p>Here is the script at the end of this chapter:</p>
|
||||
<pre><code class="language-c++">#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/tsn-node.h"
|
||||
#include "ns3/tsn-net-device.h"
|
||||
#include "ns3/cbs.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
#include "ns3/ethernet-generator.h"
|
||||
#include "ns3/clock-constant-drift.h"
|
||||
#include "ns3/gPTP.h"
|
||||
#include "ns3/ethernet-header2.h"
|
||||
#include "ns3/stream-identification-function-null.h"
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 3");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) sent!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) received!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log clock offset after correction
|
||||
static void
|
||||
ClockAfterCorrectionCallback(std::string context, Time clockValue)
|
||||
{
|
||||
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (Simulator::Now()-clockValue).GetNanoSeconds() << "ns)");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 3", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add clocks to TsnNodes
|
||||
Ptr<Clock> c0 = CreateObject<Clock>(); //perfect clock because Grandmaster
|
||||
n0->SetMainClock(c0);
|
||||
Ptr<ConstantDriftClock> c1 = CreateObject<ConstantDriftClock>();
|
||||
c1->SetAttribute("InitialOffset", TimeValue(Seconds(20)));
|
||||
c1->SetAttribute("DriftRate", DoubleValue(-50));
|
||||
c1->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n1->SetMainClock(c1);
|
||||
Ptr<ConstantDriftClock> c2 = CreateObject<ConstantDriftClock>();
|
||||
c2->SetAttribute("InitialOffset", TimeValue(Seconds(3)));
|
||||
c2->SetAttribute("DriftRate", DoubleValue(2));
|
||||
c2->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n2->SetMainClock(c2);
|
||||
Ptr<ConstantDriftClock> c3 = CreateObject<ConstantDriftClock>();
|
||||
c3->SetAttribute("InitialOffset", TimeValue(Seconds(0.5)));
|
||||
c3->SetAttribute("DriftRate", DoubleValue(-25));
|
||||
c3->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n3->SetMainClock(c3);
|
||||
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
|
||||
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
|
||||
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
|
||||
net2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
|
||||
swnet0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
|
||||
swnet1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<TsnNetDevice> swnet2 = CreateObject<TsnNetDevice>();
|
||||
swnet2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(2)));
|
||||
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(5)));
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create 8 output port FIFOs for each netDevice.
|
||||
Ptr<Cbs> cbs = CreateObject<Cbs>();
|
||||
cbs->SetTsnNetDevice(net0);
|
||||
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("20Kb/s")));
|
||||
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs); //FIFO 1
|
||||
for (int i=0; i<6; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
}
|
||||
|
||||
for (int i=0; i<8; i++){
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add and configure gPTP
|
||||
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
|
||||
gPTP0->SetNode(n0);
|
||||
gPTP0->SetMainClock(c0);
|
||||
gPTP0->AddDomain(0);
|
||||
gPTP0->AddPort(net0, GPTP::MASTER, 0);
|
||||
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
|
||||
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
|
||||
gPTP0->SetAttribute("Priority", UintegerValue(7));
|
||||
n0->AddApplication(gPTP0);
|
||||
gPTP0->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
|
||||
gPTP1->SetNode(n1);
|
||||
gPTP1->SetMainClock(c1);
|
||||
gPTP1->AddDomain(0);
|
||||
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
|
||||
gPTP1->SetAttribute("Priority", UintegerValue(7));
|
||||
n1->AddApplication(gPTP1);
|
||||
gPTP1->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP2 = CreateObject<GPTP>();
|
||||
gPTP2->SetNode(n2);
|
||||
gPTP2->SetMainClock(c2);
|
||||
gPTP2->AddDomain(0);
|
||||
gPTP2->AddPort(net2, GPTP::SLAVE, 0);
|
||||
gPTP2->SetAttribute("Priority", UintegerValue(7));
|
||||
n2->AddApplication(gPTP2);
|
||||
gPTP2->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP3 = CreateObject<GPTP>();
|
||||
gPTP3->SetNode(n3);
|
||||
gPTP3->SetMainClock(c3);
|
||||
gPTP3->AddDomain(0);
|
||||
gPTP3->AddPort(swnet0, GPTP::SLAVE, 0);
|
||||
gPTP3->AddPort(swnet1, GPTP::MASTER, 0);
|
||||
gPTP3->AddPort(swnet2, GPTP::MASTER, 0);
|
||||
gPTP3->SetAttribute("Priority", UintegerValue(7));
|
||||
n3->AddApplication(gPTP3);
|
||||
gPTP3->SetStartTime(Seconds(0));
|
||||
|
||||
//Configure TAS schedule
|
||||
swnet2->AddGclEntry(Time(Seconds(2)), 0); //All gates are close
|
||||
swnet2->AddGclEntry(Time(Seconds(3)), 2); //Only the gate of the FIFO 1 is open
|
||||
swnet2->StartTas();
|
||||
|
||||
//Add a stream identification function
|
||||
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
|
||||
uint16_t StreamHandle = 10;
|
||||
sif0->SetAttribute("VlanID", UintegerValue(1));
|
||||
sif0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
n3->AddStreamIdentificationFunction(StreamHandle, sif0, {swnet0}, {}, {}, {});
|
||||
|
||||
//PSFP configuration
|
||||
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
|
||||
sfi0->SetAttribute("StreamHandle", IntegerValue(StreamHandle));
|
||||
sfi0->SetAttribute("Priority", IntegerValue(-1)); //-1 = wildcard
|
||||
sfi0->SetAttribute("MaxSDUSize", UintegerValue(1422));
|
||||
n3->AddStreamFilter(sfi0);
|
||||
Ptr<FlowMeterInstance> fm0 = CreateObject<FlowMeterInstance>();
|
||||
fm0->SetAttribute("CIR", DataRateValue(DataRate("20Kb/s")));
|
||||
fm0->SetAttribute("CBS", UintegerValue(1400));
|
||||
fm0->SetAttribute("DropOnYellow", BooleanValue(true));
|
||||
fm0->SetAttribute("MarkAllFramesRedEnable", BooleanValue(false));
|
||||
uint16_t fmid = n3->AddFlowMeter(fm0);
|
||||
sfi0->AddFlowMeterInstanceId(fmid);
|
||||
|
||||
//Sequencing : Sequence generation
|
||||
Ptr<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
|
||||
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqf0->SetStreamHandle({StreamHandle});
|
||||
n3->AddSequenceGenerationFunction(seqf0);
|
||||
//Sequence encode
|
||||
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
|
||||
seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqEnc0->SetAttribute("Active", BooleanValue(true));
|
||||
seqEnc0->SetStreamHandle({StreamHandle});
|
||||
seqEnc0->SetPort(swnet0);
|
||||
n3->AddSequenceEncodeDecodeFunction(seqEnc0);
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet1, swnet2});
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(5));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
//Callback to display clock offset after correction
|
||||
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1)));
|
||||
gPTP2->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n2)));
|
||||
gPTP3->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n3)));
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../getting_stated/network_customization.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../getting_stated/network_customization.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr-ef4e11c1.min.js"></script>
|
||||
<script src="../mark-09e88c2c.min.js"></script>
|
||||
<script src="../searcher-c2a407aa.js"></script>
|
||||
|
||||
<script src="../clipboard-1626706a.min.js"></script>
|
||||
<script src="../highlight-abc7f01d.js"></script>
|
||||
<script src="../book-a0b12cfe.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
83
userGuide/book/highlight-493f70e1.css
Normal file
83
userGuide/book/highlight-493f70e1.css
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* An increased contrast highlighting scheme loosely based on the
|
||||
* "Base16 Atelier Dune Light" theme by Bram de Haan
|
||||
* (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
|
||||
* Original Base16 color scheme by Chris Kempson
|
||||
* (https://github.com/chriskempson/base16)
|
||||
*/
|
||||
|
||||
/* Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #575757;
|
||||
}
|
||||
|
||||
/* Red */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #d70025;
|
||||
}
|
||||
|
||||
/* Orange */
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #b21e00;
|
||||
}
|
||||
|
||||
/* Green */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #008200;
|
||||
}
|
||||
|
||||
/* Blue */
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #0030f2;
|
||||
}
|
||||
|
||||
/* Purple */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #9d00ec;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #f6f7f6;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #22863a;
|
||||
background-color: #f0fff4;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #b31d28;
|
||||
background-color: #ffeef0;
|
||||
}
|
||||
54
userGuide/book/highlight-abc7f01d.js
Normal file
54
userGuide/book/highlight-abc7f01d.js
Normal file
File diff suppressed because one or more lines are too long
259
userGuide/book/index.html
Normal file
259
userGuide/book/index.html
Normal file
@@ -0,0 +1,259 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Introduction - Eden-sim - User Guide</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="favicon-de23e50b.svg">
|
||||
<link rel="shortcut icon" href="favicon-8114d1fc.png">
|
||||
<link rel="stylesheet" href="css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "";
|
||||
const default_light_theme = "light";
|
||||
const default_dark_theme = "navy";
|
||||
window.path_to_searchindex_js = "searchindex-43c2d326.js";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="toc-81bc4348.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Eden-sim - User Guide</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
<h1 id="introduction"><a class="header" href="#introduction">Introduction</a></h1>
|
||||
<p>Eden-sim is a ns-3 simulation library for embedded TSN networks.</p>
|
||||
<p>This book is a user guide designed to assist in the creation of simulation scripts using Eden-sim. It is organised as follows:</p>
|
||||
<ul>
|
||||
<li>Getting started part to introduce the fundamental ideas of Eden-sim</li>
|
||||
<li>Advanced uses part for complex projects</li>
|
||||
</ul>
|
||||
<p>Please note that in addition to the examples presented in this guide, example simulation scripts are also available in the following folders:</p>
|
||||
<ul>
|
||||
<li>contrib/ethernet/examples</li>
|
||||
<li>contrib/real-device/examples</li>
|
||||
<li>contrib/trace/examples</li>
|
||||
<li>contrib/traffic-generator/examples</li>
|
||||
<li>contrib/tsn/examples</li>
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="next prefetch" href="prerequisites.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a rel="next prefetch" href="prerequisites.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="elasticlunr-ef4e11c1.min.js"></script>
|
||||
<script src="mark-09e88c2c.min.js"></script>
|
||||
<script src="searcher-c2a407aa.js"></script>
|
||||
|
||||
<script src="clipboard-1626706a.min.js"></script>
|
||||
<script src="highlight-abc7f01d.js"></script>
|
||||
<script src="book-a0b12cfe.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
259
userGuide/book/introduction.html
Normal file
259
userGuide/book/introduction.html
Normal file
@@ -0,0 +1,259 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Introduction - Eden-sim - User Guide</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="favicon-de23e50b.svg">
|
||||
<link rel="shortcut icon" href="favicon-8114d1fc.png">
|
||||
<link rel="stylesheet" href="css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "";
|
||||
const default_light_theme = "light";
|
||||
const default_dark_theme = "navy";
|
||||
window.path_to_searchindex_js = "searchindex-43c2d326.js";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="toc-81bc4348.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Eden-sim - User Guide</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
<h1 id="introduction"><a class="header" href="#introduction">Introduction</a></h1>
|
||||
<p>Eden-sim is a ns-3 simulation library for embedded TSN networks.</p>
|
||||
<p>This book is a user guide designed to assist in the creation of simulation scripts using Eden-sim. It is organised as follows:</p>
|
||||
<ul>
|
||||
<li>Getting started part to introduce the fundamental ideas of Eden-sim</li>
|
||||
<li>Advanced uses part for complex projects</li>
|
||||
</ul>
|
||||
<p>Please note that in addition to the examples presented in this guide, example simulation scripts are also available in the following folders:</p>
|
||||
<ul>
|
||||
<li>contrib/ethernet/examples</li>
|
||||
<li>contrib/real-device/examples</li>
|
||||
<li>contrib/trace/examples</li>
|
||||
<li>contrib/traffic-generator/examples</li>
|
||||
<li>contrib/tsn/examples</li>
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="next prefetch" href="prerequisites.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a rel="next prefetch" href="prerequisites.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="elasticlunr-ef4e11c1.min.js"></script>
|
||||
<script src="mark-09e88c2c.min.js"></script>
|
||||
<script src="searcher-c2a407aa.js"></script>
|
||||
|
||||
<script src="clipboard-1626706a.min.js"></script>
|
||||
<script src="highlight-abc7f01d.js"></script>
|
||||
<script src="book-a0b12cfe.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
7
userGuide/book/mark-09e88c2c.min.js
vendored
Normal file
7
userGuide/book/mark-09e88c2c.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
252
userGuide/book/prerequisites.html
Normal file
252
userGuide/book/prerequisites.html
Normal file
@@ -0,0 +1,252 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Prerequisites - Eden-sim - User Guide</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="favicon-de23e50b.svg">
|
||||
<link rel="shortcut icon" href="favicon-8114d1fc.png">
|
||||
<link rel="stylesheet" href="css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "";
|
||||
const default_light_theme = "light";
|
||||
const default_dark_theme = "navy";
|
||||
window.path_to_searchindex_js = "searchindex-43c2d326.js";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="toc-81bc4348.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Eden-sim - User Guide</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
<h1 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h1>
|
||||
<p>To put this guide into practice, you only need to have ns-3 and Eden-sim installed. The installation procedure for these two tools is described in Eden-sim’s README.md.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="introduction.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="getting_stated/simple_ethernet_network.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="introduction.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="getting_stated/simple_ethernet_network.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="elasticlunr-ef4e11c1.min.js"></script>
|
||||
<script src="mark-09e88c2c.min.js"></script>
|
||||
<script src="searcher-c2a407aa.js"></script>
|
||||
|
||||
<script src="clipboard-1626706a.min.js"></script>
|
||||
<script src="highlight-abc7f01d.js"></script>
|
||||
<script src="book-a0b12cfe.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1599
userGuide/book/print.html
Normal file
1599
userGuide/book/print.html
Normal file
File diff suppressed because it is too large
Load Diff
555
userGuide/book/searcher-c2a407aa.js
Normal file
555
userGuide/book/searcher-c2a407aa.js
Normal file
@@ -0,0 +1,555 @@
|
||||
'use strict';
|
||||
|
||||
/* global Mark, elasticlunr, path_to_root */
|
||||
|
||||
window.search = window.search || {};
|
||||
(function search() {
|
||||
// Search functionality
|
||||
//
|
||||
// You can use !hasFocus() to prevent keyhandling in your key
|
||||
// event handlers while the user is typing their search.
|
||||
|
||||
if (!Mark || !elasticlunr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
// IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
|
||||
if (!String.prototype.startsWith) {
|
||||
String.prototype.startsWith = function(search, pos) {
|
||||
return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
|
||||
};
|
||||
}
|
||||
|
||||
const search_wrap = document.getElementById('mdbook-search-wrapper'),
|
||||
searchbar_outer = document.getElementById('mdbook-searchbar-outer'),
|
||||
searchbar = document.getElementById('mdbook-searchbar'),
|
||||
searchresults = document.getElementById('mdbook-searchresults'),
|
||||
searchresults_outer = document.getElementById('mdbook-searchresults-outer'),
|
||||
searchresults_header = document.getElementById('mdbook-searchresults-header'),
|
||||
searchicon = document.getElementById('mdbook-search-toggle'),
|
||||
content = document.getElementById('mdbook-content'),
|
||||
|
||||
// SVG text elements don't render if inside a <mark> tag.
|
||||
mark_exclude = ['text'],
|
||||
marker = new Mark(content),
|
||||
URL_SEARCH_PARAM = 'search',
|
||||
URL_MARK_PARAM = 'highlight';
|
||||
|
||||
let current_searchterm = '',
|
||||
doc_urls = [],
|
||||
search_options = {
|
||||
bool: 'AND',
|
||||
expand: true,
|
||||
fields: {
|
||||
title: {boost: 1},
|
||||
body: {boost: 1},
|
||||
breadcrumbs: {boost: 0},
|
||||
},
|
||||
},
|
||||
searchindex = null,
|
||||
results_options = {
|
||||
teaser_word_count: 30,
|
||||
limit_results: 30,
|
||||
},
|
||||
teaser_count = 0;
|
||||
|
||||
function hasFocus() {
|
||||
return searchbar === document.activeElement;
|
||||
}
|
||||
|
||||
function removeChildren(elem) {
|
||||
while (elem.firstChild) {
|
||||
elem.removeChild(elem.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to parse a url into its building blocks.
|
||||
function parseURL(url) {
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
return {
|
||||
source: url,
|
||||
protocol: a.protocol.replace(':', ''),
|
||||
host: a.hostname,
|
||||
port: a.port,
|
||||
params: (function() {
|
||||
const ret = {};
|
||||
const seg = a.search.replace(/^\?/, '').split('&');
|
||||
for (const part of seg) {
|
||||
if (!part) {
|
||||
continue;
|
||||
}
|
||||
const s = part.split('=');
|
||||
ret[s[0]] = s[1];
|
||||
}
|
||||
return ret;
|
||||
})(),
|
||||
file: (a.pathname.match(/\/([^/?#]+)$/i) || ['', ''])[1],
|
||||
hash: a.hash.replace('#', ''),
|
||||
path: a.pathname.replace(/^([^/])/, '/$1'),
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to recreate a url string from its building blocks.
|
||||
function renderURL(urlobject) {
|
||||
let url = urlobject.protocol + '://' + urlobject.host;
|
||||
if (urlobject.port !== '') {
|
||||
url += ':' + urlobject.port;
|
||||
}
|
||||
url += urlobject.path;
|
||||
let joiner = '?';
|
||||
for (const prop in urlobject.params) {
|
||||
if (Object.prototype.hasOwnProperty.call(urlobject.params, prop)) {
|
||||
url += joiner + prop + '=' + urlobject.params[prop];
|
||||
joiner = '&';
|
||||
}
|
||||
}
|
||||
if (urlobject.hash !== '') {
|
||||
url += '#' + urlobject.hash;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
// Helper to escape html special chars for displaying the teasers
|
||||
const escapeHTML = (function() {
|
||||
const MAP = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
'\'': ''',
|
||||
};
|
||||
const repl = function(c) {
|
||||
return MAP[c];
|
||||
};
|
||||
return function(s) {
|
||||
return s.replace(/[&<>'"]/g, repl);
|
||||
};
|
||||
})();
|
||||
|
||||
function formatSearchMetric(count, searchterm) {
|
||||
if (count === 1) {
|
||||
return count + ' search result for \'' + searchterm + '\':';
|
||||
} else if (count === 0) {
|
||||
return 'No search results for \'' + searchterm + '\'.';
|
||||
} else {
|
||||
return count + ' search results for \'' + searchterm + '\':';
|
||||
}
|
||||
}
|
||||
|
||||
function formatSearchResult(result, searchterms) {
|
||||
const teaser = makeTeaser(escapeHTML(result.doc.body), searchterms);
|
||||
teaser_count++;
|
||||
|
||||
// The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor
|
||||
const url = doc_urls[result.ref].split('#');
|
||||
if (url.length === 1) { // no anchor found
|
||||
url.push('');
|
||||
}
|
||||
|
||||
// encodeURIComponent escapes all chars that could allow an XSS except
|
||||
// for '. Due to that we also manually replace ' with its url-encoded
|
||||
// representation (%27).
|
||||
const encoded_search = encodeURIComponent(searchterms.join(' ')).replace(/'/g, '%27');
|
||||
|
||||
return '<a href="' + path_to_root + url[0] + '?' + URL_MARK_PARAM + '=' + encoded_search
|
||||
+ '#' + url[1] + '" aria-details="mdbook-teaser_' + teaser_count + '">'
|
||||
+ result.doc.breadcrumbs + '</a>'
|
||||
+ '<span class="teaser" id="mdbook-teaser_' + teaser_count
|
||||
+ '" aria-label="Search Result Teaser">' + teaser + '</span>';
|
||||
}
|
||||
|
||||
function makeTeaser(body, searchterms) {
|
||||
// The strategy is as follows:
|
||||
// First, assign a value to each word in the document:
|
||||
// Words that correspond to search terms (stemmer aware): 40
|
||||
// Normal words: 2
|
||||
// First word in a sentence: 8
|
||||
// Then use a sliding window with a constant number of words and count the
|
||||
// sum of the values of the words within the window. Then use the window that got the
|
||||
// maximum sum. If there are multiple maximas, then get the last one.
|
||||
// Enclose the terms in <em>.
|
||||
const stemmed_searchterms = searchterms.map(function(w) {
|
||||
return elasticlunr.stemmer(w.toLowerCase());
|
||||
});
|
||||
const searchterm_weight = 40;
|
||||
const weighted = []; // contains elements of ["word", weight, index_in_document]
|
||||
// split in sentences, then words
|
||||
const sentences = body.toLowerCase().split('. ');
|
||||
let index = 0;
|
||||
let value = 0;
|
||||
let searchterm_found = false;
|
||||
for (const sentenceindex in sentences) {
|
||||
const words = sentences[sentenceindex].split(' ');
|
||||
value = 8;
|
||||
for (const wordindex in words) {
|
||||
const word = words[wordindex];
|
||||
if (word.length > 0) {
|
||||
for (const searchtermindex in stemmed_searchterms) {
|
||||
if (elasticlunr.stemmer(word).startsWith(
|
||||
stemmed_searchterms[searchtermindex])
|
||||
) {
|
||||
value = searchterm_weight;
|
||||
searchterm_found = true;
|
||||
}
|
||||
}
|
||||
weighted.push([word, value, index]);
|
||||
value = 2;
|
||||
}
|
||||
index += word.length;
|
||||
index += 1; // ' ' or '.' if last word in sentence
|
||||
}
|
||||
index += 1; // because we split at a two-char boundary '. '
|
||||
}
|
||||
|
||||
if (weighted.length === 0) {
|
||||
return body;
|
||||
}
|
||||
|
||||
const window_weight = [];
|
||||
const window_size = Math.min(weighted.length, results_options.teaser_word_count);
|
||||
|
||||
let cur_sum = 0;
|
||||
for (let wordindex = 0; wordindex < window_size; wordindex++) {
|
||||
cur_sum += weighted[wordindex][1];
|
||||
}
|
||||
window_weight.push(cur_sum);
|
||||
for (let wordindex = 0; wordindex < weighted.length - window_size; wordindex++) {
|
||||
cur_sum -= weighted[wordindex][1];
|
||||
cur_sum += weighted[wordindex + window_size][1];
|
||||
window_weight.push(cur_sum);
|
||||
}
|
||||
|
||||
let max_sum_window_index = 0;
|
||||
if (searchterm_found) {
|
||||
let max_sum = 0;
|
||||
// backwards
|
||||
for (let i = window_weight.length - 1; i >= 0; i--) {
|
||||
if (window_weight[i] > max_sum) {
|
||||
max_sum = window_weight[i];
|
||||
max_sum_window_index = i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
max_sum_window_index = 0;
|
||||
}
|
||||
|
||||
// add <em/> around searchterms
|
||||
const teaser_split = [];
|
||||
index = weighted[max_sum_window_index][2];
|
||||
for (let i = max_sum_window_index; i < max_sum_window_index + window_size; i++) {
|
||||
const word = weighted[i];
|
||||
if (index < word[2]) {
|
||||
// missing text from index to start of `word`
|
||||
teaser_split.push(body.substring(index, word[2]));
|
||||
index = word[2];
|
||||
}
|
||||
if (word[1] === searchterm_weight) {
|
||||
teaser_split.push('<em>');
|
||||
}
|
||||
index = word[2] + word[0].length;
|
||||
teaser_split.push(body.substring(word[2], index));
|
||||
if (word[1] === searchterm_weight) {
|
||||
teaser_split.push('</em>');
|
||||
}
|
||||
}
|
||||
|
||||
return teaser_split.join('');
|
||||
}
|
||||
|
||||
function init(config) {
|
||||
results_options = config.results_options;
|
||||
search_options = config.search_options;
|
||||
doc_urls = config.doc_urls;
|
||||
searchindex = elasticlunr.Index.load(config.index);
|
||||
|
||||
searchbar_outer.classList.remove('searching');
|
||||
|
||||
searchbar.focus();
|
||||
|
||||
const searchterm = searchbar.value.trim();
|
||||
if (searchterm !== '') {
|
||||
searchbar.classList.add('active');
|
||||
doSearch(searchterm);
|
||||
}
|
||||
}
|
||||
|
||||
function initSearchInteractions(config) {
|
||||
// Set up events
|
||||
searchicon.addEventListener('click', () => {
|
||||
searchIconClickHandler();
|
||||
}, false);
|
||||
searchbar.addEventListener('keyup', () => {
|
||||
searchbarKeyUpHandler();
|
||||
}, false);
|
||||
document.addEventListener('keydown', e => {
|
||||
globalKeyHandler(e);
|
||||
}, false);
|
||||
// If the user uses the browser buttons, do the same as if a reload happened
|
||||
window.onpopstate = () => {
|
||||
doSearchOrMarkFromUrl();
|
||||
};
|
||||
// Suppress "submit" events so the page doesn't reload when the user presses Enter
|
||||
document.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
// If reloaded, do the search or mark again, depending on the current url parameters
|
||||
doSearchOrMarkFromUrl();
|
||||
|
||||
// Exported functions
|
||||
config.hasFocus = hasFocus;
|
||||
}
|
||||
|
||||
initSearchInteractions(window.search);
|
||||
|
||||
function unfocusSearchbar() {
|
||||
// hacky, but just focusing a div only works once
|
||||
const tmp = document.createElement('input');
|
||||
tmp.setAttribute('style', 'position: absolute; opacity: 0;');
|
||||
searchicon.appendChild(tmp);
|
||||
tmp.focus();
|
||||
tmp.remove();
|
||||
}
|
||||
|
||||
// On reload or browser history backwards/forwards events, parse the url and do search or mark
|
||||
function doSearchOrMarkFromUrl() {
|
||||
// Check current URL for search request
|
||||
const url = parseURL(window.location.href);
|
||||
if (Object.prototype.hasOwnProperty.call(url.params, URL_SEARCH_PARAM)
|
||||
&& url.params[URL_SEARCH_PARAM] !== '') {
|
||||
showSearch(true);
|
||||
searchbar.value = decodeURIComponent(
|
||||
(url.params[URL_SEARCH_PARAM] + '').replace(/\+/g, '%20'));
|
||||
searchbarKeyUpHandler(); // -> doSearch()
|
||||
} else {
|
||||
showSearch(false);
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(url.params, URL_MARK_PARAM)) {
|
||||
const words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' ');
|
||||
marker.mark(words, {
|
||||
exclude: mark_exclude,
|
||||
});
|
||||
|
||||
const markers = document.querySelectorAll('mark');
|
||||
const hide = () => {
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
markers[i].classList.add('fade-out');
|
||||
window.setTimeout(() => {
|
||||
marker.unmark();
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
markers[i].addEventListener('click', hide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Eventhandler for keyevents on `document`
|
||||
function globalKeyHandler(e) {
|
||||
if (e.altKey ||
|
||||
e.ctrlKey ||
|
||||
e.metaKey ||
|
||||
e.shiftKey ||
|
||||
e.target.type === 'textarea' ||
|
||||
e.target.type === 'text' ||
|
||||
!hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
searchbar.classList.remove('active');
|
||||
setSearchUrlParameters('',
|
||||
searchbar.value.trim() !== '' ? 'push' : 'replace');
|
||||
if (hasFocus()) {
|
||||
unfocusSearchbar();
|
||||
}
|
||||
showSearch(false);
|
||||
marker.unmark();
|
||||
} else if (!hasFocus() && (e.key === 's' || e.key === '/')) {
|
||||
e.preventDefault();
|
||||
showSearch(true);
|
||||
window.scrollTo(0, 0);
|
||||
searchbar.select();
|
||||
} else if (hasFocus() && (e.key === 'ArrowDown'
|
||||
|| e.key === 'Enter')) {
|
||||
e.preventDefault();
|
||||
const first = searchresults.firstElementChild;
|
||||
if (first !== null) {
|
||||
unfocusSearchbar();
|
||||
first.classList.add('focus');
|
||||
if (e.key === 'Enter') {
|
||||
window.location.assign(first.querySelector('a'));
|
||||
}
|
||||
}
|
||||
} else if (!hasFocus() && (e.key === 'ArrowDown'
|
||||
|| e.key === 'ArrowUp'
|
||||
|| e.key === 'Enter')) {
|
||||
// not `:focus` because browser does annoying scrolling
|
||||
const focused = searchresults.querySelector('li.focus');
|
||||
if (!focused) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
if (e.key === 'ArrowDown') {
|
||||
const next = focused.nextElementSibling;
|
||||
if (next) {
|
||||
focused.classList.remove('focus');
|
||||
next.classList.add('focus');
|
||||
}
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
focused.classList.remove('focus');
|
||||
const prev = focused.previousElementSibling;
|
||||
if (prev) {
|
||||
prev.classList.add('focus');
|
||||
} else {
|
||||
searchbar.select();
|
||||
}
|
||||
} else { // Enter
|
||||
window.location.assign(focused.querySelector('a'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadSearchScript(url, id) {
|
||||
if (document.getElementById(id)) {
|
||||
return;
|
||||
}
|
||||
searchbar_outer.classList.add('searching');
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.id = id;
|
||||
script.onload = () => init(window.search);
|
||||
script.onerror = error => {
|
||||
console.error(`Failed to load \`${url}\`: ${error}`);
|
||||
};
|
||||
document.head.append(script);
|
||||
}
|
||||
|
||||
function showSearch(yes) {
|
||||
if (yes) {
|
||||
loadSearchScript(
|
||||
window.path_to_searchindex_js ||
|
||||
path_to_root + 'searchindex-43c2d326.js',
|
||||
'mdbook-search-index');
|
||||
search_wrap.classList.remove('hidden');
|
||||
searchicon.setAttribute('aria-expanded', 'true');
|
||||
} else {
|
||||
search_wrap.classList.add('hidden');
|
||||
searchicon.setAttribute('aria-expanded', 'false');
|
||||
const results = searchresults.children;
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
results[i].classList.remove('focus');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showResults(yes) {
|
||||
if (yes) {
|
||||
searchresults_outer.classList.remove('hidden');
|
||||
} else {
|
||||
searchresults_outer.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Eventhandler for search icon
|
||||
function searchIconClickHandler() {
|
||||
if (search_wrap.classList.contains('hidden')) {
|
||||
showSearch(true);
|
||||
window.scrollTo(0, 0);
|
||||
searchbar.select();
|
||||
} else {
|
||||
showSearch(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Eventhandler for keyevents while the searchbar is focused
|
||||
function searchbarKeyUpHandler() {
|
||||
const searchterm = searchbar.value.trim();
|
||||
if (searchterm !== '') {
|
||||
searchbar.classList.add('active');
|
||||
doSearch(searchterm);
|
||||
} else {
|
||||
searchbar.classList.remove('active');
|
||||
showResults(false);
|
||||
removeChildren(searchresults);
|
||||
}
|
||||
|
||||
setSearchUrlParameters(searchterm, 'push_if_new_search_else_replace');
|
||||
|
||||
// Remove marks
|
||||
marker.unmark();
|
||||
}
|
||||
|
||||
// Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and
|
||||
// `#heading-anchor`. `action` can be one of "push", "replace",
|
||||
// "push_if_new_search_else_replace" and replaces or pushes a new browser history item.
|
||||
// "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet.
|
||||
function setSearchUrlParameters(searchterm, action) {
|
||||
const url = parseURL(window.location.href);
|
||||
const first_search = !Object.prototype.hasOwnProperty.call(url.params, URL_SEARCH_PARAM);
|
||||
|
||||
if (searchterm !== '' || action === 'push_if_new_search_else_replace') {
|
||||
url.params[URL_SEARCH_PARAM] = searchterm;
|
||||
delete url.params[URL_MARK_PARAM];
|
||||
url.hash = '';
|
||||
} else {
|
||||
delete url.params[URL_MARK_PARAM];
|
||||
delete url.params[URL_SEARCH_PARAM];
|
||||
}
|
||||
// A new search will also add a new history item, so the user can go back
|
||||
// to the page prior to searching. A updated search term will only replace
|
||||
// the url.
|
||||
if (action === 'push' || action === 'push_if_new_search_else_replace' && first_search ) {
|
||||
history.pushState({}, document.title, renderURL(url));
|
||||
} else if (action === 'replace' ||
|
||||
action === 'push_if_new_search_else_replace' &&
|
||||
!first_search
|
||||
) {
|
||||
history.replaceState({}, document.title, renderURL(url));
|
||||
}
|
||||
}
|
||||
|
||||
function doSearch(searchterm) {
|
||||
// Don't search the same twice
|
||||
if (current_searchterm === searchterm) {
|
||||
return;
|
||||
}
|
||||
searchbar_outer.classList.add('searching');
|
||||
if (searchindex === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
current_searchterm = searchterm;
|
||||
|
||||
// Do the actual search
|
||||
const results = searchindex.search(searchterm, search_options);
|
||||
const resultcount = Math.min(results.length, results_options.limit_results);
|
||||
|
||||
// Display search metrics
|
||||
searchresults_header.innerText = formatSearchMetric(resultcount, searchterm);
|
||||
|
||||
// Clear and insert results
|
||||
const searchterms = searchterm.split(' ');
|
||||
removeChildren(searchresults);
|
||||
for (let i = 0; i < resultcount ; i++) {
|
||||
const resultElem = document.createElement('li');
|
||||
resultElem.innerHTML = formatSearchResult(results[i], searchterms);
|
||||
searchresults.appendChild(resultElem);
|
||||
}
|
||||
|
||||
// Display results
|
||||
showResults(true);
|
||||
searchbar_outer.classList.remove('searching');
|
||||
}
|
||||
|
||||
// Exported functions
|
||||
search.hasFocus = hasFocus;
|
||||
})(window.search);
|
||||
1
userGuide/book/searchindex-43c2d326.js
Normal file
1
userGuide/book/searchindex-43c2d326.js
Normal file
File diff suppressed because one or more lines are too long
454
userGuide/book/toc-81bc4348.js
Normal file
454
userGuide/book/toc-81bc4348.js
Normal file
@@ -0,0 +1,454 @@
|
||||
// Populate the sidebar
|
||||
//
|
||||
// This is a script, and not included directly in the page, to control the total size of the book.
|
||||
// The TOC contains an entry for each page, so if each page includes a copy of the TOC,
|
||||
// the total size of the page becomes O(n**2).
|
||||
class MDBookSidebarScrollbox extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
connectedCallback() {
|
||||
this.innerHTML = '<ol class="chapter"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="introduction.html">Introduction</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="prerequisites.html">Prerequisites</a></span></li><li class="chapter-item expanded "><li class="part-title">Getting started</li></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="getting_stated/simple_ethernet_network.html"><strong aria-hidden="true">1.</strong> Creation of a simple ethernet network</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="getting_stated/network_customization.html"><strong aria-hidden="true">2.</strong> Network customization</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="getting_stated/simple_tsn_network.html"><strong aria-hidden="true">3.</strong> Creation of a simple tsn network</a></span></li><li class="chapter-item expanded "><li class="part-title">Advanced uses</li></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><span><strong aria-hidden="true">4.</strong> Work In Progress</span></span></li></ol>';
|
||||
// Set the current, active page, and reveal it if it's hidden
|
||||
let current_page = document.location.href.toString().split('#')[0].split('?')[0];
|
||||
if (current_page.endsWith('/')) {
|
||||
current_page += 'index.html';
|
||||
}
|
||||
const links = Array.prototype.slice.call(this.querySelectorAll('a'));
|
||||
const l = links.length;
|
||||
for (let i = 0; i < l; ++i) {
|
||||
const link = links[i];
|
||||
const href = link.getAttribute('href');
|
||||
if (href && !href.startsWith('#') && !/^(?:[a-z+]+:)?\/\//.test(href)) {
|
||||
link.href = path_to_root + href;
|
||||
}
|
||||
// The 'index' page is supposed to alias the first chapter in the book.
|
||||
if (link.href === current_page
|
||||
|| i === 0
|
||||
&& path_to_root === ''
|
||||
&& current_page.endsWith('/index.html')) {
|
||||
link.classList.add('active');
|
||||
let parent = link.parentElement;
|
||||
while (parent) {
|
||||
if (parent.tagName === 'LI' && parent.classList.contains('chapter-item')) {
|
||||
parent.classList.add('expanded');
|
||||
}
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Track and set sidebar scroll position
|
||||
this.addEventListener('click', e => {
|
||||
if (e.target.tagName === 'A') {
|
||||
const clientRect = e.target.getBoundingClientRect();
|
||||
const sidebarRect = this.getBoundingClientRect();
|
||||
sessionStorage.setItem('sidebar-scroll-offset', clientRect.top - sidebarRect.top);
|
||||
}
|
||||
}, { passive: true });
|
||||
const sidebarScrollOffset = sessionStorage.getItem('sidebar-scroll-offset');
|
||||
sessionStorage.removeItem('sidebar-scroll-offset');
|
||||
if (sidebarScrollOffset !== null) {
|
||||
// preserve sidebar scroll position when navigating via links within sidebar
|
||||
const activeSection = this.querySelector('.active');
|
||||
if (activeSection) {
|
||||
const clientRect = activeSection.getBoundingClientRect();
|
||||
const sidebarRect = this.getBoundingClientRect();
|
||||
const currentOffset = clientRect.top - sidebarRect.top;
|
||||
this.scrollTop += currentOffset - parseFloat(sidebarScrollOffset);
|
||||
}
|
||||
} else {
|
||||
// scroll sidebar to current active section when navigating via
|
||||
// 'next/previous chapter' buttons
|
||||
const activeSection = document.querySelector('#mdbook-sidebar .active');
|
||||
if (activeSection) {
|
||||
activeSection.scrollIntoView({ block: 'center' });
|
||||
}
|
||||
}
|
||||
// Toggle buttons
|
||||
const sidebarAnchorToggles = document.querySelectorAll('.chapter-fold-toggle');
|
||||
function toggleSection(ev) {
|
||||
ev.currentTarget.parentElement.parentElement.classList.toggle('expanded');
|
||||
}
|
||||
Array.from(sidebarAnchorToggles).forEach(el => {
|
||||
el.addEventListener('click', toggleSection);
|
||||
});
|
||||
}
|
||||
}
|
||||
window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox);
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Support for dynamically adding headers to the sidebar.
|
||||
|
||||
(function() {
|
||||
// This is used to detect which direction the page has scrolled since the
|
||||
// last scroll event.
|
||||
let lastKnownScrollPosition = 0;
|
||||
// This is the threshold in px from the top of the screen where it will
|
||||
// consider a header the "current" header when scrolling down.
|
||||
const defaultDownThreshold = 150;
|
||||
// Same as defaultDownThreshold, except when scrolling up.
|
||||
const defaultUpThreshold = 300;
|
||||
// The threshold is a virtual horizontal line on the screen where it
|
||||
// considers the "current" header to be above the line. The threshold is
|
||||
// modified dynamically to handle headers that are near the bottom of the
|
||||
// screen, and to slightly offset the behavior when scrolling up vs down.
|
||||
let threshold = defaultDownThreshold;
|
||||
// This is used to disable updates while scrolling. This is needed when
|
||||
// clicking the header in the sidebar, which triggers a scroll event. It
|
||||
// is somewhat finicky to detect when the scroll has finished, so this
|
||||
// uses a relatively dumb system of disabling scroll updates for a short
|
||||
// time after the click.
|
||||
let disableScroll = false;
|
||||
// Array of header elements on the page.
|
||||
let headers;
|
||||
// Array of li elements that are initially collapsed headers in the sidebar.
|
||||
// I'm not sure why eslint seems to have a false positive here.
|
||||
// eslint-disable-next-line prefer-const
|
||||
let headerToggles = [];
|
||||
// This is a debugging tool for the threshold which you can enable in the console.
|
||||
let thresholdDebug = false;
|
||||
|
||||
// Updates the threshold based on the scroll position.
|
||||
function updateThreshold() {
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
const windowHeight = window.innerHeight;
|
||||
const documentHeight = document.documentElement.scrollHeight;
|
||||
|
||||
// The number of pixels below the viewport, at most documentHeight.
|
||||
// This is used to push the threshold down to the bottom of the page
|
||||
// as the user scrolls towards the bottom.
|
||||
const pixelsBelow = Math.max(0, documentHeight - (scrollTop + windowHeight));
|
||||
// The number of pixels above the viewport, at least defaultDownThreshold.
|
||||
// Similar to pixelsBelow, this is used to push the threshold back towards
|
||||
// the top when reaching the top of the page.
|
||||
const pixelsAbove = Math.max(0, defaultDownThreshold - scrollTop);
|
||||
// How much the threshold should be offset once it gets close to the
|
||||
// bottom of the page.
|
||||
const bottomAdd = Math.max(0, windowHeight - pixelsBelow - defaultDownThreshold);
|
||||
let adjustedBottomAdd = bottomAdd;
|
||||
|
||||
// Adjusts bottomAdd for a small document. The calculation above
|
||||
// assumes the document is at least twice the windowheight in size. If
|
||||
// it is less than that, then bottomAdd needs to be shrunk
|
||||
// proportional to the difference in size.
|
||||
if (documentHeight < windowHeight * 2) {
|
||||
const maxPixelsBelow = documentHeight - windowHeight;
|
||||
const t = 1 - pixelsBelow / Math.max(1, maxPixelsBelow);
|
||||
const clamp = Math.max(0, Math.min(1, t));
|
||||
adjustedBottomAdd *= clamp;
|
||||
}
|
||||
|
||||
let scrollingDown = true;
|
||||
if (scrollTop < lastKnownScrollPosition) {
|
||||
scrollingDown = false;
|
||||
}
|
||||
|
||||
if (scrollingDown) {
|
||||
// When scrolling down, move the threshold up towards the default
|
||||
// downwards threshold position. If near the bottom of the page,
|
||||
// adjustedBottomAdd will offset the threshold towards the bottom
|
||||
// of the page.
|
||||
const amountScrolledDown = scrollTop - lastKnownScrollPosition;
|
||||
const adjustedDefault = defaultDownThreshold + adjustedBottomAdd;
|
||||
threshold = Math.max(adjustedDefault, threshold - amountScrolledDown);
|
||||
} else {
|
||||
// When scrolling up, move the threshold down towards the default
|
||||
// upwards threshold position. If near the bottom of the page,
|
||||
// quickly transition the threshold back up where it normally
|
||||
// belongs.
|
||||
const amountScrolledUp = lastKnownScrollPosition - scrollTop;
|
||||
const adjustedDefault = defaultUpThreshold - pixelsAbove
|
||||
+ Math.max(0, adjustedBottomAdd - defaultDownThreshold);
|
||||
threshold = Math.min(adjustedDefault, threshold + amountScrolledUp);
|
||||
}
|
||||
|
||||
if (documentHeight <= windowHeight) {
|
||||
threshold = 0;
|
||||
}
|
||||
|
||||
if (thresholdDebug) {
|
||||
const id = 'mdbook-threshold-debug-data';
|
||||
let data = document.getElementById(id);
|
||||
if (data === null) {
|
||||
data = document.createElement('div');
|
||||
data.id = id;
|
||||
data.style.cssText = `
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
right: 10px;
|
||||
background-color: 0xeeeeee;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
`;
|
||||
document.body.appendChild(data);
|
||||
}
|
||||
data.innerHTML = `
|
||||
<table>
|
||||
<tr><td>documentHeight</td><td>${documentHeight.toFixed(1)}</td></tr>
|
||||
<tr><td>windowHeight</td><td>${windowHeight.toFixed(1)}</td></tr>
|
||||
<tr><td>scrollTop</td><td>${scrollTop.toFixed(1)}</td></tr>
|
||||
<tr><td>pixelsAbove</td><td>${pixelsAbove.toFixed(1)}</td></tr>
|
||||
<tr><td>pixelsBelow</td><td>${pixelsBelow.toFixed(1)}</td></tr>
|
||||
<tr><td>bottomAdd</td><td>${bottomAdd.toFixed(1)}</td></tr>
|
||||
<tr><td>adjustedBottomAdd</td><td>${adjustedBottomAdd.toFixed(1)}</td></tr>
|
||||
<tr><td>scrollingDown</td><td>${scrollingDown}</td></tr>
|
||||
<tr><td>threshold</td><td>${threshold.toFixed(1)}</td></tr>
|
||||
</table>
|
||||
`;
|
||||
drawDebugLine();
|
||||
}
|
||||
|
||||
lastKnownScrollPosition = scrollTop;
|
||||
}
|
||||
|
||||
function drawDebugLine() {
|
||||
if (!document.body) {
|
||||
return;
|
||||
}
|
||||
const id = 'mdbook-threshold-debug-line';
|
||||
const existingLine = document.getElementById(id);
|
||||
if (existingLine) {
|
||||
existingLine.remove();
|
||||
}
|
||||
const line = document.createElement('div');
|
||||
line.id = id;
|
||||
line.style.cssText = `
|
||||
position: fixed;
|
||||
top: ${threshold}px;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 2px;
|
||||
background-color: red;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
`;
|
||||
document.body.appendChild(line);
|
||||
}
|
||||
|
||||
function mdbookEnableThresholdDebug() {
|
||||
thresholdDebug = true;
|
||||
updateThreshold();
|
||||
drawDebugLine();
|
||||
}
|
||||
|
||||
window.mdbookEnableThresholdDebug = mdbookEnableThresholdDebug;
|
||||
|
||||
// Updates which headers in the sidebar should be expanded. If the current
|
||||
// header is inside a collapsed group, then it, and all its parents should
|
||||
// be expanded.
|
||||
function updateHeaderExpanded(currentA) {
|
||||
// Add expanded to all header-item li ancestors.
|
||||
let current = currentA.parentElement;
|
||||
while (current) {
|
||||
if (current.tagName === 'LI' && current.classList.contains('header-item')) {
|
||||
current.classList.add('expanded');
|
||||
}
|
||||
current = current.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates which header is marked as the "current" header in the sidebar.
|
||||
// This is done with a virtual Y threshold, where headers at or below
|
||||
// that line will be considered the current one.
|
||||
function updateCurrentHeader() {
|
||||
if (!headers || !headers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the classes, which will be rebuilt below.
|
||||
const els = document.getElementsByClassName('current-header');
|
||||
for (const el of els) {
|
||||
el.classList.remove('current-header');
|
||||
}
|
||||
for (const toggle of headerToggles) {
|
||||
toggle.classList.remove('expanded');
|
||||
}
|
||||
|
||||
// Find the last header that is above the threshold.
|
||||
let lastHeader = null;
|
||||
for (const header of headers) {
|
||||
const rect = header.getBoundingClientRect();
|
||||
if (rect.top <= threshold) {
|
||||
lastHeader = header;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastHeader === null) {
|
||||
lastHeader = headers[0];
|
||||
const rect = lastHeader.getBoundingClientRect();
|
||||
const windowHeight = window.innerHeight;
|
||||
if (rect.top >= windowHeight) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the anchor in the summary.
|
||||
const href = '#' + lastHeader.id;
|
||||
const a = [...document.querySelectorAll('.header-in-summary')]
|
||||
.find(element => element.getAttribute('href') === href);
|
||||
if (!a) {
|
||||
return;
|
||||
}
|
||||
|
||||
a.classList.add('current-header');
|
||||
|
||||
updateHeaderExpanded(a);
|
||||
}
|
||||
|
||||
// Updates which header is "current" based on the threshold line.
|
||||
function reloadCurrentHeader() {
|
||||
if (disableScroll) {
|
||||
return;
|
||||
}
|
||||
updateThreshold();
|
||||
updateCurrentHeader();
|
||||
}
|
||||
|
||||
|
||||
// When clicking on a header in the sidebar, this adjusts the threshold so
|
||||
// that it is located next to the header. This is so that header becomes
|
||||
// "current".
|
||||
function headerThresholdClick(event) {
|
||||
// See disableScroll description why this is done.
|
||||
disableScroll = true;
|
||||
setTimeout(() => {
|
||||
disableScroll = false;
|
||||
}, 100);
|
||||
// requestAnimationFrame is used to delay the update of the "current"
|
||||
// header until after the scroll is done, and the header is in the new
|
||||
// position.
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
// Closest is needed because if it has child elements like <code>.
|
||||
const a = event.target.closest('a');
|
||||
const href = a.getAttribute('href');
|
||||
const targetId = href.substring(1);
|
||||
const targetElement = document.getElementById(targetId);
|
||||
if (targetElement) {
|
||||
threshold = targetElement.getBoundingClientRect().bottom;
|
||||
updateCurrentHeader();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Takes the nodes from the given head and copies them over to the
|
||||
// destination, along with some filtering.
|
||||
function filterHeader(source, dest) {
|
||||
const clone = source.cloneNode(true);
|
||||
clone.querySelectorAll('mark').forEach(mark => {
|
||||
mark.replaceWith(...mark.childNodes);
|
||||
});
|
||||
dest.append(...clone.childNodes);
|
||||
}
|
||||
|
||||
// Scans page for headers and adds them to the sidebar.
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const activeSection = document.querySelector('#mdbook-sidebar .active');
|
||||
if (activeSection === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const main = document.getElementsByTagName('main')[0];
|
||||
headers = Array.from(main.querySelectorAll('h2, h3, h4, h5, h6'))
|
||||
.filter(h => h.id !== '' && h.children.length && h.children[0].tagName === 'A');
|
||||
|
||||
if (headers.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a tree of headers in the sidebar.
|
||||
|
||||
const stack = [];
|
||||
|
||||
const firstLevel = parseInt(headers[0].tagName.charAt(1));
|
||||
for (let i = 1; i < firstLevel; i++) {
|
||||
const ol = document.createElement('ol');
|
||||
ol.classList.add('section');
|
||||
if (stack.length > 0) {
|
||||
stack[stack.length - 1].ol.appendChild(ol);
|
||||
}
|
||||
stack.push({level: i + 1, ol: ol});
|
||||
}
|
||||
|
||||
// The level where it will start folding deeply nested headers.
|
||||
const foldLevel = 3;
|
||||
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
const header = headers[i];
|
||||
const level = parseInt(header.tagName.charAt(1));
|
||||
|
||||
const currentLevel = stack[stack.length - 1].level;
|
||||
if (level > currentLevel) {
|
||||
// Begin nesting to this level.
|
||||
for (let nextLevel = currentLevel + 1; nextLevel <= level; nextLevel++) {
|
||||
const ol = document.createElement('ol');
|
||||
ol.classList.add('section');
|
||||
const last = stack[stack.length - 1];
|
||||
const lastChild = last.ol.lastChild;
|
||||
// Handle the case where jumping more than one nesting
|
||||
// level, which doesn't have a list item to place this new
|
||||
// list inside of.
|
||||
if (lastChild) {
|
||||
lastChild.appendChild(ol);
|
||||
} else {
|
||||
last.ol.appendChild(ol);
|
||||
}
|
||||
stack.push({level: nextLevel, ol: ol});
|
||||
}
|
||||
} else if (level < currentLevel) {
|
||||
while (stack.length > 1 && stack[stack.length - 1].level > level) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
const li = document.createElement('li');
|
||||
li.classList.add('header-item');
|
||||
li.classList.add('expanded');
|
||||
if (level < foldLevel) {
|
||||
li.classList.add('expanded');
|
||||
}
|
||||
const span = document.createElement('span');
|
||||
span.classList.add('chapter-link-wrapper');
|
||||
const a = document.createElement('a');
|
||||
span.appendChild(a);
|
||||
a.href = '#' + header.id;
|
||||
a.classList.add('header-in-summary');
|
||||
filterHeader(header.children[0], a);
|
||||
a.addEventListener('click', headerThresholdClick);
|
||||
const nextHeader = headers[i + 1];
|
||||
if (nextHeader !== undefined) {
|
||||
const nextLevel = parseInt(nextHeader.tagName.charAt(1));
|
||||
if (nextLevel > level && level >= foldLevel) {
|
||||
const toggle = document.createElement('a');
|
||||
toggle.classList.add('chapter-fold-toggle');
|
||||
toggle.classList.add('header-toggle');
|
||||
toggle.addEventListener('click', () => {
|
||||
li.classList.toggle('expanded');
|
||||
});
|
||||
const toggleDiv = document.createElement('div');
|
||||
toggleDiv.textContent = '❱';
|
||||
toggle.appendChild(toggleDiv);
|
||||
span.appendChild(toggle);
|
||||
headerToggles.push(li);
|
||||
}
|
||||
}
|
||||
li.appendChild(span);
|
||||
|
||||
const currentParent = stack[stack.length - 1];
|
||||
currentParent.ol.appendChild(li);
|
||||
}
|
||||
|
||||
const onThisPage = document.createElement('div');
|
||||
onThisPage.classList.add('on-this-page');
|
||||
onThisPage.append(stack[0].ol);
|
||||
const activeItemSpan = activeSection.parentElement;
|
||||
activeItemSpan.after(onThisPage);
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', reloadCurrentHeader);
|
||||
document.addEventListener('scroll', reloadCurrentHeader, { passive: true });
|
||||
})();
|
||||
|
||||
31
userGuide/book/toc.html
Normal file
31
userGuide/book/toc.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light" dir="ltr">
|
||||
<head>
|
||||
<!-- sidebar iframe generated using mdBook
|
||||
|
||||
This is a frame, and not included directly in the page, to control the total size of the
|
||||
book. The TOC contains an entry for each page, so if each page includes a copy of the TOC,
|
||||
the total size of the page becomes O(n**2).
|
||||
|
||||
The frame is only used as a fallback when JS is turned off. When it's on, the sidebar is
|
||||
instead added to the main page by `toc.js` instead. The JavaScript mode is better
|
||||
because, when running in a `file:///` URL, the iframed page would not be Same-Origin as
|
||||
the rest of the page, so the sidebar and the main page theme would fall out of sync.
|
||||
-->
|
||||
<meta charset="UTF-8">
|
||||
<meta name="robots" content="noindex">
|
||||
<!-- Custom HTML head -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<link rel="stylesheet" href="css/variables-8adf115d.css">
|
||||
<link rel="stylesheet" href="css/general-2459343d.css">
|
||||
<link rel="stylesheet" href="css/chrome-ae938929.css">
|
||||
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
|
||||
<!-- Custom theme stylesheets -->
|
||||
</head>
|
||||
<body class="sidebar-iframe-inner">
|
||||
<ol class="chapter"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="introduction.html" target="_parent">Introduction</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="prerequisites.html" target="_parent">Prerequisites</a></span></li><li class="chapter-item expanded "><li class="part-title">Getting started</li></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="getting_stated/simple_ethernet_network.html" target="_parent"><strong aria-hidden="true">1.</strong> Creation of a simple ethernet network</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="getting_stated/network_customization.html" target="_parent"><strong aria-hidden="true">2.</strong> Network customization</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="getting_stated/simple_tsn_network.html" target="_parent"><strong aria-hidden="true">3.</strong> Creation of a simple tsn network</a></span></li><li class="chapter-item expanded "><li class="part-title">Advanced uses</li></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><span><strong aria-hidden="true">4.</strong> Work In Progress</span></span></li></ol>
|
||||
</body>
|
||||
</html>
|
||||
104
userGuide/book/tomorrow-night-4c0ae647.css
Normal file
104
userGuide/book/tomorrow-night-4c0ae647.css
Normal file
@@ -0,0 +1,104 @@
|
||||
/* Tomorrow Night Theme */
|
||||
/* https://github.com/jmblog/color-themes-for-highlightjs */
|
||||
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||||
/* https://github.com/jmblog/color-themes-for-highlightjs */
|
||||
|
||||
/* Tomorrow Comment */
|
||||
.hljs-comment {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
/* Tomorrow Red */
|
||||
.hljs-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-tag,
|
||||
.hljs-regexp,
|
||||
.ruby .hljs-constant,
|
||||
.xml .hljs-tag .hljs-title,
|
||||
.xml .hljs-pi,
|
||||
.xml .hljs-doctype,
|
||||
.html .hljs-doctype,
|
||||
.css .hljs-id,
|
||||
.css .hljs-class,
|
||||
.css .hljs-pseudo {
|
||||
color: #cc6666;
|
||||
}
|
||||
|
||||
/* Tomorrow Orange */
|
||||
.hljs-number,
|
||||
.hljs-preprocessor,
|
||||
.hljs-pragma,
|
||||
.hljs-built_in,
|
||||
.hljs-literal,
|
||||
.hljs-params,
|
||||
.hljs-constant {
|
||||
color: #de935f;
|
||||
}
|
||||
|
||||
/* Tomorrow Yellow */
|
||||
.ruby .hljs-class .hljs-title,
|
||||
.css .hljs-rule .hljs-attribute {
|
||||
color: #f0c674;
|
||||
}
|
||||
|
||||
/* Tomorrow Green */
|
||||
.hljs-string,
|
||||
.hljs-value,
|
||||
.hljs-inheritance,
|
||||
.hljs-header,
|
||||
.hljs-name,
|
||||
.ruby .hljs-symbol,
|
||||
.xml .hljs-cdata {
|
||||
color: #b5bd68;
|
||||
}
|
||||
|
||||
/* Tomorrow Aqua */
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.css .hljs-hexcolor {
|
||||
color: #8abeb7;
|
||||
}
|
||||
|
||||
/* Tomorrow Blue */
|
||||
.hljs-function,
|
||||
.python .hljs-decorator,
|
||||
.python .hljs-title,
|
||||
.ruby .hljs-function .hljs-title,
|
||||
.ruby .hljs-title .hljs-keyword,
|
||||
.perl .hljs-sub,
|
||||
.javascript .hljs-title,
|
||||
.coffeescript .hljs-title {
|
||||
color: #81a2be;
|
||||
}
|
||||
|
||||
/* Tomorrow Purple */
|
||||
.hljs-keyword,
|
||||
.javascript .hljs-function {
|
||||
color: #b294bb;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #1d1f21;
|
||||
color: #c5c8c6;
|
||||
}
|
||||
|
||||
.coffeescript .javascript,
|
||||
.javascript .xml,
|
||||
.tex .hljs-formula,
|
||||
.xml .javascript,
|
||||
.xml .vbscript,
|
||||
.xml .css,
|
||||
.xml .hljs-cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #c82829;
|
||||
}
|
||||
10
userGuide/src/SUMMARY.md
Normal file
10
userGuide/src/SUMMARY.md
Normal file
@@ -0,0 +1,10 @@
|
||||
[Introduction](./introduction.md)
|
||||
[Prerequisites](./prerequisites.md)
|
||||
|
||||
# Getting started
|
||||
- [Creation of a simple ethernet network](./getting_stated/simple_ethernet_network.md)
|
||||
- [Network customization](./getting_stated/network_customization.md)
|
||||
- [Creation of a simple tsn network](./getting_stated/simple_tsn_network.md)
|
||||
|
||||
# Advanced uses
|
||||
- [Work In Progress]()
|
||||
1
userGuide/src/advanced_uses/wip.md
Normal file
1
userGuide/src/advanced_uses/wip.md
Normal file
@@ -0,0 +1 @@
|
||||
# WIP
|
||||
298
userGuide/src/getting_stated/network_customization.md
Normal file
298
userGuide/src/getting_stated/network_customization.md
Normal file
@@ -0,0 +1,298 @@
|
||||
# Network customization
|
||||
|
||||
## Introduction
|
||||
In the previous chapter, we wrote a simulation script for a simple Ethernet network. In this chapter, we will discuss how to customize it using the attributes of the different classes used.
|
||||
|
||||
## Attributes in ns-3
|
||||
To organize access and configuration of instantiated object parameters, ns-3 uses its own attribute system.
|
||||
|
||||
In the previous chapter, we already implemented this overlay using the “SetAttribute()” function to configure the application. This function has two arguments. The first is a string corresponding to the name of the attribute to be changed. The second is the value to be assigned to this attribute.
|
||||
|
||||
The various attributes available for each object are visible in the C++ implementation of each object. Here is an example of the EthernetChannel object described in contrib/ethernet/model/ethernet-channel.cc.
|
||||
|
||||
```c++
|
||||
TypeId
|
||||
EthernetChannel::GetTypeId()
|
||||
{
|
||||
static TypeId tid =
|
||||
TypeId("ns3::EthernetChannel")
|
||||
.SetParent<Channel>()
|
||||
.SetGroupName("Ethernet")
|
||||
.AddConstructor<EthernetChannel>()
|
||||
.AddAttribute("Delay",
|
||||
"Propagation delay through the channel",
|
||||
TimeValue(NanoSeconds(25)),
|
||||
MakeTimeAccessor(&EthernetChannel::m_delay),
|
||||
MakeTimeChecker())
|
||||
.AddTraceSource("TxRxEthernet",
|
||||
"Trace source indicating transmission of packet "
|
||||
"from the EthernetChannel, used by the Animation "
|
||||
"interface.",
|
||||
MakeTraceSourceAccessor(&EthernetChannel::m_txrxEthernet),
|
||||
"ns3::EthernetChannel::TxRxAnimationCallback");
|
||||
return tid;
|
||||
}
|
||||
```
|
||||
We can see that this object has only one attribute called Delay, which allows you to configure the propagation delay. It is of type Time and has a default value of 25ns.
|
||||
|
||||
Thus, it is possible to configure the propagation delay in the previous simulation script as follows:
|
||||
```c++
|
||||
[...]
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
[...]
|
||||
```
|
||||
|
||||
## Common attributes
|
||||
|
||||
Here are examples of the attributes most often used in Ethernet network simulations.
|
||||
```c++
|
||||
[...]
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
net2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
swnet0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
swnet1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
swnet2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
[...]
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
[...]
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(2)));
|
||||
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(5)));
|
||||
n3->AddDevice(sw);
|
||||
|
||||
[...]
|
||||
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
Ptr<DropTailQueue<Packet>> q0 = CreateObject<DropTailQueue<Packet>>();
|
||||
q0->SetAttribute("MaxSize", QueueSizeValue(QueueSize("250p")));
|
||||
net0->SetQueue(q0);
|
||||
|
||||
[...]
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("InterFrame", TimeValue(MilliSeconds(20)));
|
||||
app0->SetAttribute("Jitter", TimeValue(MilliSeconds(50)));
|
||||
app0->SetAttribute("Offset", TimeValue(Seconds(1)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
app0->SetAttribute("DEI", UintegerValue(0));
|
||||
[...]
|
||||
|
||||
```
|
||||
|
||||
## Final simulation script
|
||||
Here is the simulation script you should have at the end of this chapter.
|
||||
```c++
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/ethernet-net-device.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
#include "ns3/ethernet-generator.h"
|
||||
|
||||
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 2");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 2", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<Node> n0 = CreateObject<Node>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<Node> n1 = CreateObject<Node>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<Node> n2 = CreateObject<Node>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<Node> n3 = CreateObject<Node>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
net2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
swnet0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
swnet1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
swnet2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(2)));
|
||||
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(5)));
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet2});
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("InterFrame", TimeValue(MilliSeconds(20)));
|
||||
app0->SetAttribute("Jitter", TimeValue(MilliSeconds(50)));
|
||||
app0->SetAttribute("Offset", TimeValue(Seconds(1)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
app0->SetAttribute("DEI", UintegerValue(0));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
385
userGuide/src/getting_stated/simple_ethernet_network.md
Normal file
385
userGuide/src/getting_stated/simple_ethernet_network.md
Normal file
@@ -0,0 +1,385 @@
|
||||
# Creation of a simple ethernet network
|
||||
|
||||
## Introduction
|
||||
To perform a simulation with ns-3, you need to write a C++ program that describes the simulation to be performed. In this chapter, we will create such a simulation script able to simulate an Ethernet network composed of one switch to which three end stations are connected.
|
||||
|
||||
## First simulation script
|
||||
Let's start by creating a script that runs a 10-second simulation. To do this, create a file called chapter1.cc in the scratch folder located in the ns-3 folder. In this file, copy the following lines:
|
||||
```c++
|
||||
#include "ns3/simulator.h"
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 1");
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 1", LOG_LEVEL_INFO);
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
To run it, simply execute the following command from the folder where ns-3 is installed.
|
||||
```console
|
||||
./ns3 run scratch/chapter1.cc
|
||||
```
|
||||
Congratulations, you have run your first simulation with ns-3! However, this simulation does not simulate anything.
|
||||
|
||||
## Topology description
|
||||
Before simulating our Ethernet network, we must first describe it. To do this, let's start by adding the necessary dependencies.
|
||||
```c++
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/ethernet-net-device.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
|
||||
[...]
|
||||
```
|
||||
Next, the topology is described as follows:
|
||||
```c++
|
||||
[...]
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 1", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<Node> n0 = CreateObject<Node>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<Node> n1 = CreateObject<Node>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<Node> n2 = CreateObject<Node>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<Node> n3 = CreateObject<Node>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create two output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
[...]
|
||||
```
|
||||
In this code, the following six steps are performed:
|
||||
1. *Creating nodes:* In this example, four nodes are created.
|
||||
2. *Creating NetDevices:* A NetDevice is a network interface in ns-3 terminology. Here, one NetDevice is created and added to each end-station node, and three NetDevices are created and added to the switch node.
|
||||
3. *Creating channels:* A channel is a link in ns-3 terminology in the case of the wired network considered here. Thus, three channels are created and attached to different NetDevices to connect the three end stations to the switch.
|
||||
4. *Creating SwitchNetDevices:* The SwitchNetDevice is the object that will perform switching between the switch's NetDevices. Here, a SwitchNetDevice object is created and attached to the switch node. The switch ports are also attached to it.
|
||||
5. *Allocation of Mac addresses:* For our example, four mac addresses are allocated using the iterator provided by ns-3.
|
||||
6. *Creation of output port queues:* For our example, two FIFOs are instantiated per NetDevices.
|
||||
|
||||
At this stage, it is possible to run a simulation, but the output will be the same since no traffic is simulated.
|
||||
|
||||
## Traffic simulation
|
||||
To send Ethernet frames in our network, we need to instantiate applications. To instantiate these applications, we must first add the following dependency.
|
||||
```c++
|
||||
#include “ns3/ethernet-generator.h”
|
||||
```
|
||||
This dependency is part of Eden-sim and is used to transmit Q-Tagged Ethernet frames.
|
||||
|
||||
Next, let's add an application that sends a burst of two frames with a payload of 1400 bytes, a priority of 1, and a VLAN ID of 1 every 5 seconds. This application is hosted on node ES1. The frames are destined for ES3. Here is the code used to instantiate such an application:
|
||||
```c++
|
||||
[...]
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
[...]
|
||||
```
|
||||
With this simulation script complete, you can run a simulation... And you should see nothing new.
|
||||
|
||||
Indeed, there is no indication for ns-3 to produce any output. So our frames are sent in our simulated network, but we have no output to confirm it. To display logs about our frames in the console, let's add a callback for the transmission and one for the reception.
|
||||
```c++
|
||||
[...]
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 1");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
[...]
|
||||
```
|
||||
|
||||
```c++
|
||||
[...]
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
[...]
|
||||
```
|
||||
|
||||
If you run the simulation again, you should get the following output.
|
||||
```console
|
||||
$ ./ns3 run scratch/book.cc
|
||||
[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+5s ES1:ES1#01 : Pkt #2 sent!
|
||||
+5s ES1:ES1#01 : Pkt #3 sent!
|
||||
End of the simulation
|
||||
```
|
||||
|
||||
We can see that four frames are successfully sent by ES1 but are never received by ES3. This is because, after the first hop, the frames are received by the switch but it does not know which port to send them to. It is therefore necessary to add a static forwarding configuration to our switch.
|
||||
|
||||
## Network configuration
|
||||
Unlike plug-and-play switches, the switches simulated by Eden-sim are designed for critical embedded networks. To guarantee network determinism, these switches do not implement dynamic mechanisms such as mac learning. They must therefore be configured statically before the simulation begins.
|
||||
|
||||
Here, the only configuration missing to transmit frames to ES3 is the configuration of the static switching table. It is possible to add an entry to this table as follows:
|
||||
|
||||
```c++
|
||||
[...]
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet2});
|
||||
[...]
|
||||
```
|
||||
|
||||
After this configuration, running the simulation produces the following output, which finally indicates the transmission and reception of frames.
|
||||
```console
|
||||
$ ./ns3 run scratch/book.cc
|
||||
[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+2.293e-05s ES3:ES3#01 : Pkt #0 received!
|
||||
+3.4466e-05s ES3:ES3#01 : Pkt #1 received!
|
||||
+5s ES1:ES1#01 : Pkt #2 sent!
|
||||
+5s ES1:ES1#01 : Pkt #3 sent!
|
||||
+5.00002s ES3:ES3#01 : Pkt #2 received!
|
||||
+5.00003s ES3:ES3#01 : Pkt #3 received!
|
||||
End of the simulation
|
||||
```
|
||||
|
||||
## Final simulation script
|
||||
Here is the simulation script you should have at the end of this chapter.
|
||||
```c++
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/ethernet-net-device.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
#include "ns3/ethernet-generator.h"
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 1");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 1", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<Node> n0 = CreateObject<Node>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<Node> n1 = CreateObject<Node>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<Node> n2 = CreateObject<Node>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<Node> n3 = CreateObject<Node>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
for (int i=0; i<2; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet2});
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(2));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
740
userGuide/src/getting_stated/simple_tsn_network.md
Normal file
740
userGuide/src/getting_stated/simple_tsn_network.md
Normal file
@@ -0,0 +1,740 @@
|
||||
# Creation of a simple tsn network
|
||||
|
||||
## Introduction
|
||||
In this chapter, we will explore how to transform the Ethernet network simulation script from the previous chapter into a TSN network simulation script. Then, we will discuss how to instantiate and configure the various TSN mechanisms supported by the simulation library.
|
||||
|
||||
Note that the operation and configuration of TSN mechanisms will not be explained in depth. This chapter assumes that the reader is familiar with the details of the mechanisms implemented.
|
||||
|
||||
## From Ethernet to TSN simulation
|
||||
Let's start by importing two new objects: TsnNode and TsnNetDevice.
|
||||
```c++
|
||||
[...]
|
||||
#include "ns3/tsn-node.h"
|
||||
#include "ns3/tsn-net-device.h"
|
||||
[...]
|
||||
```
|
||||
|
||||
Next, it is necessary to replace the node and EthernetNetDevice objects with their TSN versions in order to instantiate the various TSN mechanisms described below.
|
||||
```c++
|
||||
[...]
|
||||
//Create four nodes
|
||||
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<TsnNetDevice> swnet2 = CreateObject<TsnNetDevice>();
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
[...]
|
||||
```
|
||||
At this stage, it is possible to run a simulation, but the result will be identical to a simple Ethernet network.
|
||||
|
||||
To simplify the illustration of the latency control mechanisms (CBS and TAS), we can make the following modifications to the application:
|
||||
```c++
|
||||
[...]
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(5));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
[...]
|
||||
```
|
||||
|
||||
The output of a simulation should look like this:
|
||||
```console
|
||||
[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+0s ES1:ES1#01 : Pkt #2 sent!
|
||||
+0s ES1:ES1#01 : Pkt #3 sent!
|
||||
+0s ES1:ES1#01 : Pkt #4 sent!
|
||||
+0.000233905s ES3:ES3#01 : Pkt #0 received!
|
||||
+0.000349265s ES3:ES3#01 : Pkt #1 received!
|
||||
+0.000464625s ES3:ES3#01 : Pkt #2 received!
|
||||
+0.000579985s ES3:ES3#01 : Pkt #3 received!
|
||||
+0.000695345s ES3:ES3#01 : Pkt #4 received!
|
||||
+5s ES1:ES1#01 : Pkt #5 sent!
|
||||
+5s ES1:ES1#01 : Pkt #6 sent!
|
||||
+5s ES1:ES1#01 : Pkt #7 sent!
|
||||
+5s ES1:ES1#01 : Pkt #8 sent!
|
||||
+5s ES1:ES1#01 : Pkt #9 sent!
|
||||
+5.00023s ES3:ES3#01 : Pkt #5 received!
|
||||
+5.00035s ES3:ES3#01 : Pkt #6 received!
|
||||
+5.00046s ES3:ES3#01 : Pkt #7 received!
|
||||
+5.00058s ES3:ES3#01 : Pkt #8 received!
|
||||
+5.0007s ES3:ES3#01 : Pkt #9 received!
|
||||
End of the simulation
|
||||
```
|
||||
|
||||
## CBS
|
||||
To instantiate a CBS, we must first add its dependency.
|
||||
```c++
|
||||
#include "ns3/cbs.h"
|
||||
```
|
||||
|
||||
It is then possible to instantiate a CBS and link it to an output port queue. In the following example, we instantiate the CBS on the output port of the transmitting end station.
|
||||
```c++
|
||||
[...]
|
||||
//Create 2 output port FIFOs for each netDevice.
|
||||
Ptr<Cbs> cbs = CreateObject<Cbs>();
|
||||
cbs->SetTsnNetDevice(net0);
|
||||
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("20Kb/s")));
|
||||
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs); //FIFO 1
|
||||
|
||||
for (int i=0; i<2; i++){
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
[...]
|
||||
```
|
||||
|
||||
When launching the simulation, we can now see that the reception of burst frames is spread out over time due to the waiting time imposed by the CBS credit reload on the emission port.
|
||||
```console
|
||||
[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+0s ES1:ES1#01 : Pkt #2 sent!
|
||||
+0s ES1:ES1#01 : Pkt #3 sent!
|
||||
+0s ES1:ES1#01 : Pkt #4 sent!
|
||||
+0.000233905s ES3:ES3#01 : Pkt #0 received!
|
||||
+0.577031s ES3:ES3#01 : Pkt #1 received!
|
||||
+1.15383s ES3:ES3#01 : Pkt #2 received!
|
||||
+1.73063s ES3:ES3#01 : Pkt #3 received!
|
||||
+2.30743s ES3:ES3#01 : Pkt #4 received!
|
||||
+5s ES1:ES1#01 : Pkt #5 sent!
|
||||
+5s ES1:ES1#01 : Pkt #6 sent!
|
||||
+5s ES1:ES1#01 : Pkt #7 sent!
|
||||
+5s ES1:ES1#01 : Pkt #8 sent!
|
||||
+5s ES1:ES1#01 : Pkt #9 sent!
|
||||
+5.00023s ES3:ES3#01 : Pkt #5 received!
|
||||
+5.57703s ES3:ES3#01 : Pkt #6 received!
|
||||
+6.15383s ES3:ES3#01 : Pkt #7 received!
|
||||
+6.73063s ES3:ES3#01 : Pkt #8 received!
|
||||
+7.30743s ES3:ES3#01 : Pkt #9 received!
|
||||
End of the simulation
|
||||
```
|
||||
|
||||
## TAS
|
||||
To instanciate TAS, we need to add a clock to the TsnNode, add GCL entries to the net device and add 8 FIFOs to the output ports. It can be done as follows:
|
||||
```c++
|
||||
[...]
|
||||
//Add a perfect clock to the SW node
|
||||
n3->AddClock(CreateObject<Clock>());
|
||||
[...]
|
||||
//Create 8 output port FIFOs for each netDevice.
|
||||
Ptr<Cbs> cbs = CreateObject<Cbs>();
|
||||
cbs->SetTsnNetDevice(net0);
|
||||
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("20Kb/s")));
|
||||
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs); //FIFO 1
|
||||
for (int i=0; i<6; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
}
|
||||
|
||||
for (int i=0; i<8; i++){
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
[...]
|
||||
//Configure TAS schedule
|
||||
swnet2->AddGclEntry(Time(Seconds(2)), 0); //All gates are close
|
||||
swnet2->AddGclEntry(Time(Seconds(3)), 2); //Only the gate of the FIFO 1 is open
|
||||
swnet2->StartTas();
|
||||
[...]
|
||||
```
|
||||
|
||||
In this example, a TAS schedule is added to the switch's output port, used by the flow. This schedule is designed to delay the frames of the two bursts by approximately two seconds compared to the previous example. The result obtained is as follows.
|
||||
```console
|
||||
[0/2] Re-checking globbed directories...
|
||||
[2/2] Linking CXX executable ../build/scratch/ns3.40-book-default
|
||||
Start of the simulation
|
||||
+0s ES1:ES1#01 : Pkt #0 sent!
|
||||
+0s ES1:ES1#01 : Pkt #1 sent!
|
||||
+0s ES1:ES1#01 : Pkt #2 sent!
|
||||
+0s ES1:ES1#01 : Pkt #3 sent!
|
||||
+0s ES1:ES1#01 : Pkt #4 sent!
|
||||
+2.00011s ES3:ES3#01 : Pkt #0 received!
|
||||
+2.00023s ES3:ES3#01 : Pkt #1 received!
|
||||
+2.00035s ES3:ES3#01 : Pkt #2 received!
|
||||
+2.00046s ES3:ES3#01 : Pkt #3 received!
|
||||
+2.30743s ES3:ES3#01 : Pkt #4 received!
|
||||
+5s ES1:ES1#01 : Pkt #5 sent!
|
||||
+5s ES1:ES1#01 : Pkt #6 sent!
|
||||
+5s ES1:ES1#01 : Pkt #7 sent!
|
||||
+5s ES1:ES1#01 : Pkt #8 sent!
|
||||
+5s ES1:ES1#01 : Pkt #9 sent!
|
||||
+7.00011s ES3:ES3#01 : Pkt #5 received!
|
||||
+7.00023s ES3:ES3#01 : Pkt #6 received!
|
||||
+7.00035s ES3:ES3#01 : Pkt #7 received!
|
||||
+7.00046s ES3:ES3#01 : Pkt #8 received!
|
||||
+7.30743s ES3:ES3#01 : Pkt #9 received!
|
||||
End of the simulation
|
||||
```
|
||||
|
||||
## gPTP
|
||||
To synchronize the nodes in our network using gPTP, several changes must be made. Let's start with the includes.
|
||||
```c++
|
||||
[...]
|
||||
#include "ns3/clock-constant-drift.h"
|
||||
#include "ns3/gPTP.h"
|
||||
#include "ns3/ethernet-header2.h"
|
||||
[...]
|
||||
```
|
||||
|
||||
Next, let's modify the callbacks that log packet transmission and reception so that events are only logged if the VLAN ID matches the VLAN ID of the flows going from ES1 to ES3. This change prevents the output from being flooded with logs concerning the transmission or reception of synchronization frames. We also add a callback to log the difference between the perfect clock (the simulation time) and the device clock after each correction to verify that gPTP is working properly.
|
||||
```c++
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " sent!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log clock offset after correction
|
||||
static void
|
||||
ClockAfterCorrectionCallback(std::string context, Time clockValue)
|
||||
{
|
||||
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (Simulator::Now()-clockValue).GetNanoSeconds() << "ns)");
|
||||
}
|
||||
```
|
||||
|
||||
Next, we replace the perfect clock previously instantiated on n3(SW) in the TAS section with clock instantiation on the various network devices. Note that the clock instantiated on ES1(n0) is a perfect clock (i.e., it corresponds to the simulation time) since it acts as the Grandmaster.
|
||||
```c++
|
||||
[...]
|
||||
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add clocks to TsnNodes
|
||||
Ptr<Clock> c0 = CreateObject<Clock>(); //perfect clock because Grandmaster
|
||||
n0->SetMainClock(c0);
|
||||
Ptr<ConstantDriftClock> c1 = CreateObject<ConstantDriftClock>();
|
||||
c1->SetAttribute("InitialOffset", TimeValue(Seconds(20)));
|
||||
c1->SetAttribute("DriftRate", DoubleValue(-50));
|
||||
c1->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n1->SetMainClock(c1);
|
||||
Ptr<ConstantDriftClock> c2 = CreateObject<ConstantDriftClock>();
|
||||
c2->SetAttribute("InitialOffset", TimeValue(Seconds(3)));
|
||||
c2->SetAttribute("DriftRate", DoubleValue(2));
|
||||
c2->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n2->SetMainClock(c2);
|
||||
Ptr<ConstantDriftClock> c3 = CreateObject<ConstantDriftClock>();
|
||||
c3->SetAttribute("InitialOffset", TimeValue(Seconds(0.5)));
|
||||
c3->SetAttribute("DriftRate", DoubleValue(-25));
|
||||
c3->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n3->SetMainClock(c3);
|
||||
|
||||
[...]
|
||||
```
|
||||
|
||||
Then, we add and configure the gPTP instances on the different nodes. Note that the implementation of gPTP in Eden-sim only supports static configuration of gPTP (i.e. no BTCA).
|
||||
```c++
|
||||
[...]
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
|
||||
gPTP0->SetNode(n0);
|
||||
gPTP0->SetMainClock(c0);
|
||||
gPTP0->AddDomain(0);
|
||||
gPTP0->AddPort(net0, GPTP::MASTER, 0);
|
||||
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
|
||||
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
|
||||
gPTP0->SetAttribute("Priority", UintegerValue(7));
|
||||
n0->AddApplication(gPTP0);
|
||||
gPTP0->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
|
||||
gPTP1->SetNode(n1);
|
||||
gPTP1->SetMainClock(c1);
|
||||
gPTP1->AddDomain(0);
|
||||
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
|
||||
gPTP1->SetAttribute("Priority", UintegerValue(7));
|
||||
n1->AddApplication(gPTP1);
|
||||
gPTP1->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP2 = CreateObject<GPTP>();
|
||||
gPTP2->SetNode(n2);
|
||||
gPTP2->SetMainClock(c2);
|
||||
gPTP2->AddDomain(0);
|
||||
gPTP2->AddPort(net2, GPTP::SLAVE, 0);
|
||||
gPTP2->SetAttribute("Priority", UintegerValue(7));
|
||||
n2->AddApplication(gPTP2);
|
||||
gPTP2->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP3 = CreateObject<GPTP>();
|
||||
gPTP3->SetNode(n3);
|
||||
gPTP3->SetMainClock(c3);
|
||||
gPTP3->AddDomain(0);
|
||||
gPTP3->AddPort(swnet0, GPTP::SLAVE, 0);
|
||||
gPTP3->AddPort(swnet1, GPTP::MASTER, 0);
|
||||
gPTP3->AddPort(swnet2, GPTP::MASTER, 0);
|
||||
gPTP3->SetAttribute("Priority", UintegerValue(7));
|
||||
n3->AddApplication(gPTP3);
|
||||
gPTP3->SetStartTime(Seconds(0));
|
||||
```
|
||||
|
||||
And finally, we add the callbacks.
|
||||
```c++
|
||||
[...]
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
//Callback to display clock offset after correction
|
||||
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1)));
|
||||
gPTP2->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n2)));
|
||||
gPTP3->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n3)));
|
||||
[...]
|
||||
```
|
||||
When running the simulation, we can observe that gPTP effectively mitigates clock drift thanks to periodic synchronization on SW and ES2. However, we note that there is no gPTP log for ES3. This is due to the TAS configuration, which never opens the FIFO7 gate used by synchronization messages. Of course, such a configuration has no place in a network that is intended to be functional, but in this example it illustrates the impact of the TAS configuration on gPTP packets.
|
||||
|
||||
## Stream Identification
|
||||
Before using PSFP or FRER, it is necessary to implement an identification function. In this section, we will use a null Stream identification function on the switch port connected to ES1. This function will then be used in the following two sections to implement PSFP in order to validate the flow contract and to replicate frames using FRER.
|
||||
|
||||
Let's start by importing this function.
|
||||
```c++
|
||||
[...]
|
||||
#include "ns3/stream-identification-function-null.h"
|
||||
[...]
|
||||
```
|
||||
|
||||
Next, let's create and add the identification function on port swnet0 in input and outfacing mode.
|
||||
```c++
|
||||
[...]
|
||||
swnet2->StartTas();
|
||||
|
||||
//Add a stream identification function
|
||||
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
|
||||
uint16_t StreamHandle = 10;
|
||||
sif0->SetAttribute("VlanID", UintegerValue(1));
|
||||
sif0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
n3->AddStreamIdentificationFunction(StreamHandle, sif0, {swnet0}, {}, {}, {});
|
||||
[...]
|
||||
```
|
||||
|
||||
## PSFP
|
||||
Now that we have an identification function capable of identifying frames going from ES1 to ES3 with VLAN ID 1, we can set up a PSFP instance to validate compliance with a network usage contract. Currently, the simulator implements the stream filter and the flow meter. They can be added and configured as follows.
|
||||
```c++
|
||||
[...]
|
||||
n3->AddStreamIdentificationFunction(StreamHandle, sif0, {swnet0}, {}, {}, {});
|
||||
|
||||
//PSFP configuration
|
||||
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
|
||||
sfi0->SetAttribute("StreamHandle", IntegerValue(StreamHandle));
|
||||
sfi0->SetAttribute("Priority", IntegerValue(-1)); //-1 = wildcard
|
||||
sfi0->SetAttribute("MaxSDUSize", UintegerValue(1422));
|
||||
n3->AddStreamFilter(sfi0);
|
||||
Ptr<FlowMeterInstance> fm0 = CreateObject<FlowMeterInstance>();
|
||||
fm0->SetAttribute("CIR", DataRateValue(DataRate("20Kb/s")));
|
||||
fm0->SetAttribute("CBS", UintegerValue(1400));
|
||||
fm0->SetAttribute("DropOnYellow", BooleanValue(true));
|
||||
fm0->SetAttribute("MarkAllFramesRedEnable", BooleanValue(false));
|
||||
uint16_t fmid = n3->AddFlowMeter(fm0);
|
||||
sfi0->AddFlowMeterInstanceId(fmid);
|
||||
[...]
|
||||
```
|
||||
After these changes, running the simulation does not produce a different output from previous runs. However, by changing the parameters of the emitting application to increase its bandwidth consumption (e.g., increasing the packet size) or by increasing the idle slope of the CBS, it is possible to observe that packets that do not comply with the contract are never received as they are discarded.
|
||||
|
||||
## FRER
|
||||
And finally, let's instantiate FRER. However, the topology we are working with in this chapter does not have multiple paths between ES1 and ES3. So in this section, we will replicate the frames on the output ports of the switch connected to ES2 and ES3 to illustrate the philosophy behind FRER instantiation. We will only detail the replication part. The elimination part is detailed in the example contrib/tsn/examples/tsn-switched-withFRER.cc, which has a topology much more suited to the use of FRER.
|
||||
|
||||
Replication with FRER is based on two functions: Sequence Generation Function and Sequence Encode/Decode Function. These two functions are created and configured as follows.
|
||||
|
||||
```c++
|
||||
[...]
|
||||
sfi0->AddFlowMeterInstanceId(fmid);
|
||||
|
||||
//Sequencing : Sequence generation
|
||||
Ptr<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
|
||||
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqf0->SetStreamHandle({StreamHandle});
|
||||
n3->AddSequenceGenerationFunction(seqf0);
|
||||
//Sequence encode
|
||||
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
|
||||
seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqEnc0->SetAttribute("Active", BooleanValue(true));
|
||||
seqEnc0->SetStreamHandle({StreamHandle});
|
||||
seqEnc0->SetPort(swnet0);
|
||||
n3->AddSequenceEncodeDecodeFunction(seqEnc0);
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet1, swnet2});
|
||||
[...]
|
||||
```
|
||||
With this configuration, replication is achieved by forwarding to two different ports. This simplifies configuration by not using the FRER splitting function. However, it is necessary to change the configuration of the forwarding table as shown in the previous listing.
|
||||
|
||||
In order to validate the correct operation of the replication (i.e., the addition of the R-TAG), modify the callbacks as follows to display the frame size at transmission and reception.
|
||||
```c++
|
||||
[...]
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) sent!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) received!");
|
||||
}
|
||||
}
|
||||
[...]
|
||||
```
|
||||
And now, when running the simulation script, we observe an increase in frame size of 6 bytes (the size of R-TAG) between transmission and reception, as illustrated below.
|
||||
```console
|
||||
[...]
|
||||
+5s ES1:ES1#01 : Pkt #215(1422bytes) sent!
|
||||
[...]
|
||||
+7.30743s ES3:ES3#01 : Pkt #215(1428bytes) received!
|
||||
[...]
|
||||
```
|
||||
|
||||
## Conclusion and final simulation script
|
||||
In this section, we have implemented the various TSN mechanisms of Eden-sim.
|
||||
|
||||
Note that the examples found in contrib/tsn/examples/ illustrate more complicated configurations and implement different traces to log information about the mechanism's operation. These examples are a good means of further exploring the uses of these TSN mechanisms.
|
||||
|
||||
Here is the script at the end of this chapter:
|
||||
```c++
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/drop-tail-queue.h"
|
||||
|
||||
#include "ns3/tsn-node.h"
|
||||
#include "ns3/tsn-net-device.h"
|
||||
#include "ns3/cbs.h"
|
||||
#include "ns3/ethernet-channel.h"
|
||||
#include "ns3/switch-net-device.h"
|
||||
#include "ns3/ethernet-generator.h"
|
||||
#include "ns3/clock-constant-drift.h"
|
||||
#include "ns3/gPTP.h"
|
||||
#include "ns3/ethernet-header2.h"
|
||||
#include "ns3/stream-identification-function-null.h"
|
||||
|
||||
using namespace ns3;
|
||||
NS_LOG_COMPONENT_DEFINE("Chapter 3");
|
||||
|
||||
//A callback to log the pkt emission
|
||||
static void
|
||||
MacTxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) sent!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log the pkt reception
|
||||
static void
|
||||
MacRxCallback(std::string context, Ptr<const Packet> p)
|
||||
{
|
||||
Ptr<Packet> pkt = p->Copy();
|
||||
EthernetHeader2 ethHeader;
|
||||
pkt->RemoveHeader(ethHeader);
|
||||
if (ethHeader.GetVid() == 1) {
|
||||
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(" << p->GetSize() << "bytes) received!");
|
||||
}
|
||||
}
|
||||
|
||||
//A callback to log clock offset after correction
|
||||
static void
|
||||
ClockAfterCorrectionCallback(std::string context, Time clockValue)
|
||||
{
|
||||
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (Simulator::Now()-clockValue).GetNanoSeconds() << "ns)");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
//Enable logging
|
||||
LogComponentEnable("Chapter 3", LOG_LEVEL_INFO);
|
||||
|
||||
//Create four nodes
|
||||
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
|
||||
Names::Add("ES1", n0);
|
||||
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
|
||||
Names::Add("ES2", n1);
|
||||
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
|
||||
Names::Add("ES3", n2);
|
||||
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
|
||||
Names::Add("SW", n3);
|
||||
|
||||
//Create and add clocks to TsnNodes
|
||||
Ptr<Clock> c0 = CreateObject<Clock>(); //perfect clock because Grandmaster
|
||||
n0->SetMainClock(c0);
|
||||
Ptr<ConstantDriftClock> c1 = CreateObject<ConstantDriftClock>();
|
||||
c1->SetAttribute("InitialOffset", TimeValue(Seconds(20)));
|
||||
c1->SetAttribute("DriftRate", DoubleValue(-50));
|
||||
c1->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n1->SetMainClock(c1);
|
||||
Ptr<ConstantDriftClock> c2 = CreateObject<ConstantDriftClock>();
|
||||
c2->SetAttribute("InitialOffset", TimeValue(Seconds(3)));
|
||||
c2->SetAttribute("DriftRate", DoubleValue(2));
|
||||
c2->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n2->SetMainClock(c2);
|
||||
Ptr<ConstantDriftClock> c3 = CreateObject<ConstantDriftClock>();
|
||||
c3->SetAttribute("InitialOffset", TimeValue(Seconds(0.5)));
|
||||
c3->SetAttribute("DriftRate", DoubleValue(-25));
|
||||
c3->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
|
||||
n3->SetMainClock(c3);
|
||||
|
||||
|
||||
//Create and add a netDevice to each end-station node
|
||||
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
|
||||
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n0->AddDevice(net0);
|
||||
Names::Add("ES1#01", net0);
|
||||
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
|
||||
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n1->AddDevice(net1);
|
||||
Names::Add("ES2#01", net1);
|
||||
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
|
||||
net2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n2->AddDevice(net2);
|
||||
Names::Add("ES3#01", net2);
|
||||
//Create and add a netDevice to each switch port
|
||||
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
|
||||
swnet0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet0);
|
||||
Names::Add("SW#01", swnet0);
|
||||
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
|
||||
swnet1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet1);
|
||||
Names::Add("SW#02", swnet1);
|
||||
Ptr<TsnNetDevice> swnet2 = CreateObject<TsnNetDevice>();
|
||||
swnet2->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
|
||||
n3->AddDevice(swnet2);
|
||||
Names::Add("SW#03", swnet2);
|
||||
|
||||
//Create Ethernet Channels and connect switch to the end-stations
|
||||
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
|
||||
channel0->SetAttribute("Delay", TimeValue(NanoSeconds(50)));
|
||||
net0->Attach(channel0);
|
||||
swnet0->Attach(channel0);
|
||||
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
|
||||
channel1->SetAttribute("Delay", TimeValue(NanoSeconds(75)));
|
||||
net1->Attach(channel1);
|
||||
swnet1->Attach(channel1);
|
||||
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
|
||||
channel2->SetAttribute("Delay", TimeValue(NanoSeconds(100)));
|
||||
net2->Attach(channel2);
|
||||
swnet2->Attach(channel2);
|
||||
|
||||
//Create and add a switch net device to the switch node
|
||||
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
|
||||
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(2)));
|
||||
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(5)));
|
||||
n3->AddDevice(sw);
|
||||
sw->AddSwitchPort(swnet0);
|
||||
sw->AddSwitchPort(swnet1);
|
||||
sw->AddSwitchPort(swnet2);
|
||||
|
||||
//Allocate Mac addresses to the netDevices
|
||||
net0->SetAddress(Mac48Address::Allocate());
|
||||
net1->SetAddress(Mac48Address::Allocate());
|
||||
net2->SetAddress(Mac48Address::Allocate());
|
||||
sw->SetAddress(Mac48Address::Allocate());
|
||||
|
||||
//Create 8 output port FIFOs for each netDevice.
|
||||
Ptr<Cbs> cbs = CreateObject<Cbs>();
|
||||
cbs->SetTsnNetDevice(net0);
|
||||
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("20Kb/s")));
|
||||
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs); //FIFO 1
|
||||
for (int i=0; i<6; i++){
|
||||
net0->SetQueue(CreateObject<DropTailQueue<Packet>>()); //FIFO 0
|
||||
}
|
||||
|
||||
for (int i=0; i<8; i++){
|
||||
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
|
||||
}
|
||||
|
||||
//Add and configure gPTP
|
||||
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
|
||||
gPTP0->SetNode(n0);
|
||||
gPTP0->SetMainClock(c0);
|
||||
gPTP0->AddDomain(0);
|
||||
gPTP0->AddPort(net0, GPTP::MASTER, 0);
|
||||
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
|
||||
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
|
||||
gPTP0->SetAttribute("Priority", UintegerValue(7));
|
||||
n0->AddApplication(gPTP0);
|
||||
gPTP0->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
|
||||
gPTP1->SetNode(n1);
|
||||
gPTP1->SetMainClock(c1);
|
||||
gPTP1->AddDomain(0);
|
||||
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
|
||||
gPTP1->SetAttribute("Priority", UintegerValue(7));
|
||||
n1->AddApplication(gPTP1);
|
||||
gPTP1->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP2 = CreateObject<GPTP>();
|
||||
gPTP2->SetNode(n2);
|
||||
gPTP2->SetMainClock(c2);
|
||||
gPTP2->AddDomain(0);
|
||||
gPTP2->AddPort(net2, GPTP::SLAVE, 0);
|
||||
gPTP2->SetAttribute("Priority", UintegerValue(7));
|
||||
n2->AddApplication(gPTP2);
|
||||
gPTP2->SetStartTime(Seconds(0));
|
||||
Ptr<GPTP> gPTP3 = CreateObject<GPTP>();
|
||||
gPTP3->SetNode(n3);
|
||||
gPTP3->SetMainClock(c3);
|
||||
gPTP3->AddDomain(0);
|
||||
gPTP3->AddPort(swnet0, GPTP::SLAVE, 0);
|
||||
gPTP3->AddPort(swnet1, GPTP::MASTER, 0);
|
||||
gPTP3->AddPort(swnet2, GPTP::MASTER, 0);
|
||||
gPTP3->SetAttribute("Priority", UintegerValue(7));
|
||||
n3->AddApplication(gPTP3);
|
||||
gPTP3->SetStartTime(Seconds(0));
|
||||
|
||||
//Configure TAS schedule
|
||||
swnet2->AddGclEntry(Time(Seconds(2)), 0); //All gates are close
|
||||
swnet2->AddGclEntry(Time(Seconds(3)), 2); //Only the gate of the FIFO 1 is open
|
||||
swnet2->StartTas();
|
||||
|
||||
//Add a stream identification function
|
||||
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
|
||||
uint16_t StreamHandle = 10;
|
||||
sif0->SetAttribute("VlanID", UintegerValue(1));
|
||||
sif0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
n3->AddStreamIdentificationFunction(StreamHandle, sif0, {swnet0}, {}, {}, {});
|
||||
|
||||
//PSFP configuration
|
||||
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
|
||||
sfi0->SetAttribute("StreamHandle", IntegerValue(StreamHandle));
|
||||
sfi0->SetAttribute("Priority", IntegerValue(-1)); //-1 = wildcard
|
||||
sfi0->SetAttribute("MaxSDUSize", UintegerValue(1422));
|
||||
n3->AddStreamFilter(sfi0);
|
||||
Ptr<FlowMeterInstance> fm0 = CreateObject<FlowMeterInstance>();
|
||||
fm0->SetAttribute("CIR", DataRateValue(DataRate("20Kb/s")));
|
||||
fm0->SetAttribute("CBS", UintegerValue(1400));
|
||||
fm0->SetAttribute("DropOnYellow", BooleanValue(true));
|
||||
fm0->SetAttribute("MarkAllFramesRedEnable", BooleanValue(false));
|
||||
uint16_t fmid = n3->AddFlowMeter(fm0);
|
||||
sfi0->AddFlowMeterInstanceId(fmid);
|
||||
|
||||
//Sequencing : Sequence generation
|
||||
Ptr<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
|
||||
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqf0->SetStreamHandle({StreamHandle});
|
||||
n3->AddSequenceGenerationFunction(seqf0);
|
||||
//Sequence encode
|
||||
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
|
||||
seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing
|
||||
seqEnc0->SetAttribute("Active", BooleanValue(true));
|
||||
seqEnc0->SetStreamHandle({StreamHandle});
|
||||
seqEnc0->SetPort(swnet0);
|
||||
n3->AddSequenceEncodeDecodeFunction(seqEnc0);
|
||||
|
||||
//Add a forwarding table entry
|
||||
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 1, {swnet1, swnet2});
|
||||
|
||||
//Application description
|
||||
//ES1 -> ES3 with priority 1
|
||||
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
|
||||
app0->Setup(net0);
|
||||
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
|
||||
app0->SetAttribute("BurstSize", UintegerValue(5));
|
||||
app0->SetAttribute("PayloadSize", UintegerValue(1400));
|
||||
app0->SetAttribute("Period", TimeValue(Seconds(5)));
|
||||
app0->SetAttribute("VlanID", UintegerValue(1));
|
||||
app0->SetAttribute("PCP", UintegerValue(1));
|
||||
n0->AddApplication(app0);
|
||||
app0->SetStartTime(Seconds(0));
|
||||
app0->SetStopTime(Seconds(10));
|
||||
|
||||
//Callback declarations
|
||||
//Callback to display the packet sent log
|
||||
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
|
||||
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
|
||||
//Callback to display the packet received log
|
||||
context = Names::FindName(n2) + ":" + Names::FindName(net2);
|
||||
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
|
||||
//Callback to display clock offset after correction
|
||||
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1)));
|
||||
gPTP2->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n2)));
|
||||
gPTP3->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n3)));
|
||||
|
||||
|
||||
//Execute the simulation
|
||||
NS_LOG_INFO("Start of the simulation");
|
||||
Simulator::Stop(Seconds(10));
|
||||
Simulator::Run();
|
||||
Simulator::Destroy();
|
||||
NS_LOG_INFO("End of the simulation");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
14
userGuide/src/introduction.md
Normal file
14
userGuide/src/introduction.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Introduction
|
||||
|
||||
Eden-sim is a ns-3 simulation library for embedded TSN networks.
|
||||
|
||||
This book is a user guide designed to assist in the creation of simulation scripts using Eden-sim. It is organised as follows:
|
||||
- Getting started part to introduce the fundamental ideas of Eden-sim
|
||||
- Advanced uses part for complex projects
|
||||
|
||||
Please note that in addition to the examples presented in this guide, example simulation scripts are also available in the following folders:
|
||||
- contrib/ethernet/examples
|
||||
- contrib/real-device/examples
|
||||
- contrib/trace/examples
|
||||
- contrib/traffic-generator/examples
|
||||
- contrib/tsn/examples
|
||||
3
userGuide/src/prerequisites.md
Normal file
3
userGuide/src/prerequisites.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Prerequisites
|
||||
|
||||
To put this guide into practice, you only need to have ns-3 and Eden-sim installed. The installation procedure for these two tools is described in Eden-sim's README.md.
|
||||
Reference in New Issue
Block a user