blob: 2fb58dfc23c9cc462a7ddf9a6d05dc271fe273f8 [file] [log] [blame]
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui" /><title>Revolutionizing IIoT with Apache PLC4X</title><meta name="description" content="A short intro into what benefits the industry could have with PLC4X" /><meta name="keywords" content="IIoT, IoT" /><meta name="author" content="Your Name" /><link rel="stylesheet" href="reveal.js-3.9.2/css/reset.css" /><link rel="stylesheet" href="reveal.js-3.9.2/css/reveal.css" /><link href="css/c3.min.css" rel="stylesheet" /><script src="js/d3.min.js" charset="utf-8"></script><script src="js/c3.min.js"></script><link rel="stylesheet" href="reveal.js-3.9.2/css/theme/apache.css" id="theme" /><!--This CSS is generated by the Asciidoctor reveal.js converter to further integrate AsciiDoc's existing semantic with reveal.js--><style type="text/css">.reveal div.right {
float: right
}
/* source blocks */
.reveal .listingblock.stretch > .content {
height: 100%
}
.reveal .listingblock.stretch > .content > pre {
height: 100%
}
.reveal .listingblock.stretch > .content > pre > code {
height: 100%;
max-height: 100%
}
/* auto-animate feature */
/* hide the scrollbar when auto-animating source blocks */
.reveal pre[data-auto-animate-target] {
overflow: hidden;
}
.reveal pre[data-auto-animate-target] code {
overflow: hidden;
}
/* add a min width to avoid horizontal shift on line numbers */
code.hljs .hljs-ln-line.hljs-ln-n {
min-width: 1.25em;
}
/* tables */
table {
border-collapse: collapse;
border-spacing: 0
}
table {
margin-bottom: 1.25em;
border: solid 1px #dedede
}
table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td {
padding: .5em .625em .625em;
font-size: inherit;
text-align: left
}
table tr th, table tr td {
padding: .5625em .625em;
font-size: inherit
}
table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td {
display: table-cell;
line-height: 1.6
}
td.tableblock > .content {
margin-bottom: 1.25em
}
td.tableblock > .content > :last-child {
margin-bottom: -1.25em
}
table.tableblock, th.tableblock, td.tableblock {
border: 0 solid #dedede
}
table.grid-all > thead > tr > .tableblock, table.grid-all > tbody > tr > .tableblock {
border-width: 0 1px 1px 0
}
table.grid-all > tfoot > tr > .tableblock {
border-width: 1px 1px 0 0
}
table.grid-cols > * > tr > .tableblock {
border-width: 0 1px 0 0
}
table.grid-rows > thead > tr > .tableblock, table.grid-rows > tbody > tr > .tableblock {
border-width: 0 0 1px
}
table.grid-rows > tfoot > tr > .tableblock {
border-width: 1px 0 0
}
table.grid-all > * > tr > .tableblock:last-child, table.grid-cols > * > tr > .tableblock:last-child {
border-right-width: 0
}
table.grid-all > tbody > tr:last-child > .tableblock, table.grid-all > thead:last-child > tr > .tableblock, table.grid-rows > tbody > tr:last-child > .tableblock, table.grid-rows > thead:last-child > tr > .tableblock {
border-bottom-width: 0
}
table.frame-all {
border-width: 1px
}
table.frame-sides {
border-width: 0 1px
}
table.frame-topbot, table.frame-ends {
border-width: 1px 0
}
.reveal table th.halign-left, .reveal table td.halign-left {
text-align: left
}
.reveal table th.halign-right, .reveal table td.halign-right {
text-align: right
}
.reveal table th.halign-center, .reveal table td.halign-center {
text-align: center
}
.reveal table th.valign-top, .reveal table td.valign-top {
vertical-align: top
}
.reveal table th.valign-bottom, .reveal table td.valign-bottom {
vertical-align: bottom
}
.reveal table th.valign-middle, .reveal table td.valign-middle {
vertical-align: middle
}
table thead th, table tfoot th {
font-weight: bold
}
tbody tr th {
display: table-cell;
line-height: 1.6
}
tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p {
font-weight: bold
}
thead {
display: table-header-group
}
.reveal table.grid-none th, .reveal table.grid-none td {
border-bottom: 0 !important
}
/* kbd macro */
kbd {
font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace;
display: inline-block;
color: rgba(0, 0, 0, .8);
font-size: .65em;
line-height: 1.45;
background: #f7f7f7;
border: 1px solid #ccc;
-webkit-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em white inset;
box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em #fff inset;
margin: 0 .15em;
padding: .2em .5em;
vertical-align: middle;
position: relative;
top: -.1em;
white-space: nowrap
}
.keyseq kbd:first-child {
margin-left: 0
}
.keyseq kbd:last-child {
margin-right: 0
}
/* callouts */
.conum[data-value] {
display: inline-block;
color: #fff !important;
background: rgba(0, 0, 0, .8);
-webkit-border-radius: 50%;
border-radius: 50%;
text-align: center;
font-size: .75em;
width: 1.67em;
height: 1.67em;
line-height: 1.67em;
font-family: "Open Sans", "DejaVu Sans", sans-serif;
font-style: normal;
font-weight: bold
}
.conum[data-value] * {
color: #fff !important
}
.conum[data-value] + b {
display: none
}
.conum[data-value]:after {
content: attr(data-value)
}
pre .conum[data-value] {
position: relative;
top: -.125em
}
b.conum * {
color: inherit !important
}
.conum:not([data-value]):empty {
display: none
}
/* Callout list */
.hdlist > table, .colist > table {
border: 0;
background: none
}
.hdlist > table > tbody > tr, .colist > table > tbody > tr {
background: none
}
td.hdlist1, td.hdlist2 {
vertical-align: top;
padding: 0 .625em
}
td.hdlist1 {
font-weight: bold;
padding-bottom: 1.25em
}
/* Disabled from Asciidoctor CSS because it caused callout list to go under the
* source listing when .stretch is applied (see #335)
* .literalblock+.colist,.listingblock+.colist{margin-top:-.5em} */
.colist td:not([class]):first-child {
padding: .4em .75em 0;
line-height: 1;
vertical-align: top
}
.colist td:not([class]):first-child img {
max-width: none
}
.colist td:not([class]):last-child {
padding: .25em 0
}
/* Override Asciidoctor CSS that causes issues with reveal.js features */
.reveal .hljs table {
border: 0
}
/* Callout list rows would have a bottom border with some reveal.js themes (see #335) */
.reveal .colist > table th, .reveal .colist > table td {
border-bottom: 0
}
/* Fixes line height with Highlight.js source listing when linenums enabled (see #331) */
.reveal .hljs table thead tr th, .reveal .hljs table tfoot tr th, .reveal .hljs table tbody tr td, .reveal .hljs table tr td, .reveal .hljs table tfoot tr td {
line-height: inherit
}
/* Columns layout */
.columns .slide-content {
display: flex;
}
.columns.wrap .slide-content {
flex-wrap: wrap;
}
.columns.is-vcentered .slide-content {
align-items: center;
}
.columns .slide-content > .column {
display: block;
flex-basis: 0;
flex-grow: 1;
flex-shrink: 1;
}
.columns .slide-content > .column > * {
padding: .75rem;
}
/* See #353 */
.columns.wrap .slide-content > .column {
flex-basis: auto;
}
.columns .slide-content > .column.is-full {
flex: none;
width: 100%;
}
.columns .slide-content > .column.is-four-fifths {
flex: none;
width: 80%;
}
.columns .slide-content > .column.is-three-quarters {
flex: none;
width: 75%;
}
.columns .slide-content > .column.is-two-thirds {
flex: none;
width: 66.6666%;
}
.columns .slide-content > .column.is-three-fifths {
flex: none;
width: 60%;
}
.columns .slide-content > .column.is-half {
flex: none;
width: 50%;
}
.columns .slide-content > .column.is-two-fifths {
flex: none;
width: 40%;
}
.columns .slide-content > .column.is-one-third {
flex: none;
width: 33.3333%;
}
.columns .slide-content > .column.is-one-quarter {
flex: none;
width: 25%;
}
.columns .slide-content > .column.is-one-fifth {
flex: none;
width: 20%;
}
.columns .slide-content > .column.has-text-left {
text-align: left;
}
.columns .slide-content > .column.has-text-justified {
text-align: justify;
}
.columns .slide-content > .column.has-text-right {
text-align: right;
}
.columns .slide-content > .column.has-text-left {
text-align: left;
}
.columns .slide-content > .column.has-text-justified {
text-align: justify;
}
.columns .slide-content > .column.has-text-right {
text-align: right;
}
.text-left {
text-align: left !important
}
.text-right {
text-align: right !important
}
.text-center {
text-align: center !important
}
.text-justify {
text-align: justify !important
}
.footnotes {
border-top: 1px solid rgba(0, 0, 0, 0.2);
padding: 0.5em 0 0 0;
font-size: 0.65em;
margin-top: 4em;
}
.byline {
font-size:.8em
}
ul.byline {
list-style-type: none;
}
ul.byline li + li {
margin-top: 0.25em;
}
</style><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" /><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/v4-shims.min.css" /><!--Printing and PDF exports--><script>var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? "reveal.js-3.9.2/css/print/pdf.css" : "reveal.js-3.9.2/css/print/paper.css";
document.getElementsByTagName( 'head' )[0].appendChild( link );</script></head><body><div class="header"><div class="left"></div><div class="right"></div></div><div class="reveal"><div class="slides"><section id="revolutionizing-iiot-with-apache-plc4x"><h2>Revolutionizing IIoT with Apache PLC4X</h2><div class="slide-content"><div class="imageblock"><img src="images/apache_plc4x_logo.png" alt="apache plc4x logo" /></div></div></section>
<section id="who-am-i"><h2>Who am I?</h2><div class="slide-content"><div class="paragraph"><p>Your Name<br />
Your role<br />
Your email<br /></p></div>
<aside class="notes"><div class="paragraph"><p>Please add your own details here</p></div></aside></div></section>
<section id="what-am-i-going-to-cover"><h2>What am I going to cover?</h2><div class="slide-content"><div class="ulist"><ul><li><p>What is Apache PLC4X?</p></li><li><p>What can you do with PLC4X?</p></li><li><p>What could you do beyond that?</p></li></ul></div></div></section>
<section><div class="slide-content"><div class="imageblock"><img src="images/apache_plc4x_logo.png" alt="apache plc4x logo" /></div>
<div class="quoteblock"><blockquote>PLC4X is a set of libraries for communicating with industrial programmable logic controllers (PLCs) using a variety of protocols but with a shared API.</blockquote><div class="attribution">&#8212; Apache PLC4X Project Statement</div></div></div></section>
<section id="apache-plc4x"><h2>Apache PLC4X</h2><div class="slide-content"><div class="ulist"><ul><li><p><a href="https://plc4x.apache.org" class="bare">https://plc4x.apache.org</a></p></li><li><p>Allows writing software for any type of PLC</p></li><li><p>When changing the PLC, only configuration needs to be adjusted</p></li><li><p>Strong growing number of supported protocols</p></li><li><p>Strong growing number of programming languages</p></li><li><p>Strong growing number of integration modules</p></li><li><p>Support of features, which protocols generally don&#8217;t support</p></li><li><p>Make PLC Data available in a unified way</p></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>Modbus generally only allows bits and short values</p></li><li><p>Unified data: PlcValues all handled equally over all protocols</p></li><li><p>Unified Namespace</p></li></ul></div></aside></div></section>
<section id="plc4x-supported-operations"><h2>PLC4X Supported Operations</h2><div class="slide-content"><div class="ulist"><ul><li><p>Read <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>Write <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>Subscription <span class="icon green"><i class="fa fa-check"></i></span></p><div class="ulist"><ul><li><p>Cyclic</p></li><li><p>On Value Change</p></li><li><p>Event/Alarm</p></li></ul></div></li><li><p>Discovery <span class="icon orange"><i class="fa fa-hammer"></i></span></p></li><li><p>Browse <span class="icon orange"><i class="fa fa-hammer"></i></span></p></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>We&#8217;re currently working a lot on Discovery and Browsing</p></li><li><p>Discovery: Which devices do I have and how can I connect to them?</p></li><li><p>Browse: Which resources do these devices have?</p></li></ul></div></aside></div></section>
<section id="plc4x-supported-protocols"><h2>PLC4X Supported Protocols</h2><div class="slide-content"><div class="ulist west"><ul><li><p>Siemens S7 (Step7) <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>Beckhoff ADS <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>Modbus (TCP/RTU) <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>EtherNet/IP <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>OPC-UA <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>Firmata <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>KnxNet/IP <span class="icon green"><i class="fa fa-check"></i></span></p></li><li><p>CAN <span class="icon green"><i class="fa fa-check"></i></span></p></li></ul></div>
<div class="ulist east"><ul><li><p>ProfiNet <span class="icon orange"><i class="fa fa-wrench"></i></span></p></li><li><p>Allen-Bradley AB-ETH <span class="icon orange"><i class="fa fa-wrench"></i></span></p></li><li><p>Allen-Bradley DF1 <span class="icon orange"><i class="fa fa-wrench"></i></span></p></li><li><p>BacNet <span class="icon orange"><i class="fa fa-wrench"></i></span></p></li><li><p>Emerson DeltaV <span class="icon orange"><i class="fa fa-wrench"></i></span></p></li><li><p>Luxtronic <span class="icon orange"><i class="fa fa-wrench"></i></span></p></li><li><p>Siemens S7 (TIA) <span class="icon red"><i class="fa fa-comments-o"></i></span></p></li><li><p>&#8230;&#8203;</p></li></ul></div></div></section>
<section id="polyglot-plc4x"><h2>Polyglot PLC4X</h2><div class="slide-content"><div class="ulist"><ul><li><p>Supporting multiple languages was planned from the start</p><div class="ulist"><ul><li><p>The <code>X</code> in PLC4X stands for the multiple Languages</p></li></ul></div></li><li><p>Writing PLC driver code is easy</p></li><li><p>Understanding the protocols is hard</p></li><li><p>As soon as a protocol is "understood", create drivers in many languages</p></li><li><p>Cross-compiling/wrapping just wouldn&#8217;t feel right</p></li><li><p>Heavy lifting done by our code-generation framework</p></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>Cross Compiling:</p><div class="ulist"><ul><li><p>Increased Size</p></li><li><p>Strange API feeling in other language</p></li></ul></div></li></ul></div></aside></div></section>
<section id="what-can-you-do-with-plc4x"><h2>What can you do with PLC4X?</h2></section>
<section id="making-machine-data-visible"><h2>Making machine data visible</h2><div class="slide-content"><div class="imageblock"><img src="images/sp-data-explorer-3.png" alt="sp data explorer 3" /></div></div></section>
<section id="overall-equipment-efficiency"><h2>Overall Equipment Efficiency</h2><div class="slide-content"><div class="imageblock"><img src="images/digital-cockpit.png" alt="digital cockpit" /></div></div></section>
<section id="predictive-maintenance"><h2>Predictive Maintenance</h2><div class="slide-content"><div class="ulist"><ul><li><p>Predict when a device needs maintenance</p></li><li><p>Predict probable failures</p></li><li><p>Run the machine at the right speed to minimize</p><div class="ulist"><ul><li><p>Wear &amp; tear</p></li><li><p>Energy-consumption</p></li><li><p>Resource usage</p></li></ul></div></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>Increased energy consumption &#8594; needs maintenance</p></li></ul></div></aside></div></section>
<section id="what-could-you-do-beyond-that"><h2>What could you do beyond that?</h2><div class="slide-content"><div class="ulist"><ul><li><p>Following use-cases not yet fully implemented</p></li><li><p>From my experience, what the industry is really missing</p></li><li><p>Would need some extra work</p></li></ul></div></div></section>
<section id="plc-simulator-for-unit-tests"><h2>PLC "Simulator" for unit-tests</h2><div class="slide-content"><div class="ulist"><ul><li><p>In manufacturing engeineering almost no unit-testing done</p></li><li><p>If testing is done, then only on the real machine</p></li><li><p>Usually only happy-path testing</p></li><li><p>Solution:</p><div class="ulist"><ul><li><p>PLC4X stand-alone application: "Simulator"</p></li><li><p>Built to speak with PLC4X drivers (Not full protocol support)</p></li><li><p>Use PLC4X to set values in the "PLC" from the Unit-Test</p></li><li><p>Test PLC4X Application against the simulator without needing real machinery</p></li></ul></div></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>The simulator already exists</p></li><li><p>Need to implement:</p><div class="ulist"><ul><li><p>General memory management</p></li><li><p>"Subscriptions"</p></li></ul></div></li></ul></div></aside></div></section>
<section id="historian"><h2>Historian</h2><div class="slide-content"><div class="ulist"><ul><li><p>Historians are sort of ancient time-series databases</p></li><li><p>Mostly required for regulations</p></li><li><p>Extremely expensive</p></li><li><p>Don&#8217;t scale</p></li><li><p>Solution:</p><div class="ulist"><ul><li><p>Use Apache PLC4X to get the data</p></li><li><p>Use Apache IoTDB to store the data</p></li><li><p>Add interfaces to replicate Historian interfaces</p></li></ul></div></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>We&#8217;re already working on Apache Historian</p></li><li><p>However mainly me working on it</p></li></ul></div></aside></div></section>
<section id="digital-twin"><h2>Digital Twin</h2><div class="slide-content"><div class="ulist"><ul><li><p>Everyone is talking about Digital Twins</p></li><li><p>No good products available yet</p></li><li><p>Not expecting any OT-vendor product to be usable</p></li><li><p>Solution:</p><div class="ulist"><ul><li><p>Use Apache PLC4X to get the data</p></li><li><p>Use Apache IoTDB to store the timeseries data</p></li><li><p>Use Eclipse Dito to store the current state</p></li><li><p>Use Apache KIE (incubating) to do smart things with the data</p></li></ul></div></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>IoTDB is great for time-series data</p></li><li><p>Ditto is great for current state and dependencies</p></li><li><p>KIE (Knowledge is everything): Contains Drools, jBPM</p></li></ul></div></aside></div></section>
<section id="mes-replacement"><h2>MES replacement</h2><div class="slide-content"><div class="ulist"><ul><li><p>MES = Manufacturing Execution System</p></li><li><p>Big, fat, slow and don&#8217;t scale</p></li><li><p>Products today way more customizable:</p><div class="ulist"><ul><li><p>A lot more interaction between PLC and MES needed</p></li><li><p>A lot more to do for the MES</p></li><li><p>MES becomes the bottleneck</p></li></ul></div></li><li><p>Solution:</p><div class="ulist"><ul><li><p>Building a distributed MES based on principle of highly scalable distributed systems</p></li></ul></div></li></ul></div>
<aside class="notes"><div class="ulist"><ul><li><p>Cars today usually so customizable, that there are only very few equal configurations</p></li><li><p>PLC needs to ask MES about options for every produced item</p></li></ul></div></aside></div></section>
<section id="want-to-make-this-happen"><h2>Want to make this happen?</h2><div class="slide-content"><div class="ulist"><ul><li><p><a href="mailto:dev@plc4x.apache.org">dev@plc4x.apache.org</a></p></li><li><p>Twitter: @ApachePLC4X</p></li></ul></div></div></section></div></div><div class="footer"><div class="left"></div><div class="right"></div></div><script src="reveal.js-3.9.2/js/reveal.js"></script><script>Array.prototype.slice.call(document.querySelectorAll('.slides section')).forEach(function(slide) {
if (slide.getAttribute('data-background-color')) return;
// user needs to explicitly say he wants CSS color to override otherwise we might break custom css or theme (#226)
if (!(slide.classList.contains('canvas') || slide.classList.contains('background'))) return;
var bgColor = getComputedStyle(slide).backgroundColor;
if (bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
slide.setAttribute('data-background-color', bgColor);
slide.style.backgroundColor = 'transparent';
}
});
// More info about config & dependencies:
// - https://github.com/hakimel/reveal.js#configuration
// - https://github.com/hakimel/reveal.js#dependencies
Reveal.initialize({
// Display presentation control arrows
controls: true,
// Help the user learn the controls by providing hints, for example by
// bouncing the down arrow when they first encounter a vertical slide
controlsTutorial: true,
// Determines where controls appear, "edges" or "bottom-right"
controlsLayout: 'bottom-right',
// Visibility rule for backwards navigation arrows; "faded", "hidden"
// or "visible"
controlsBackArrows: 'faded',
// Display a presentation progress bar
progress: true,
// Display the page number of the current slide
slideNumber: 'true',
// Control which views the slide number displays on
showSlideNumber: 'all',
// Add the current slide number to the URL hash so that reloading the
// page/copying the URL will return you to the same slide
hash: false,
// Push each slide change to the browser history. Implies `hash: true`
history: true,
// Enable keyboard shortcuts for navigation
keyboard: true,
// Enable the slide overview mode
overview: true,
// Disables the default reveal.js slide layout so that you can use custom CSS layout
disableLayout: false,
// Vertical centering of slides
center: false,
// Enables touch navigation on devices with touch input
touch: true,
// Loop the presentation
loop: false,
// Change the presentation direction to be RTL
rtl: false,
// See https://github.com/hakimel/reveal.js/#navigation-mode
navigationMode: 'default',
// Randomizes the order of slides each time the presentation loads
shuffle: false,
// Turns fragments on and off globally
fragments: true,
// Flags whether to include the current fragment in the URL,
// so that reloading brings you to the same fragment position
fragmentInURL: false,
// Flags if the presentation is running in an embedded mode,
// i.e. contained within a limited portion of the screen
embedded: false,
// Flags if we should show a help overlay when the questionmark
// key is pressed
help: true,
// Flags if speaker notes should be visible to all viewers
showNotes: false,
// Global override for autolaying embedded media (video/audio/iframe)
// - null: Media will only autoplay if data-autoplay is present
// - true: All media will autoplay, regardless of individual setting
// - false: No media will autoplay, regardless of individual setting
autoPlayMedia: null,
// Global override for preloading lazy-loaded iframes
// - null: Iframes with data-src AND data-preload will be loaded when within
// the viewDistance, iframes with only data-src will be loaded when visible
// - true: All iframes with data-src will be loaded when within the viewDistance
// - false: All iframes with data-src will be loaded only when visible
preloadIframes: null,
// Number of milliseconds between automatically proceeding to the
// next slide, disabled when set to 0, this value can be overwritten
// by using a data-autoslide attribute on your slides
autoSlide: 0,
// Stop auto-sliding after user input
autoSlideStoppable: true,
// Use this method for navigation when auto-sliding
autoSlideMethod: Reveal.navigateNext,
// Specify the average time in seconds that you think you will spend
// presenting each slide. This is used to show a pacing timer in the
// speaker view
defaultTiming: 120,
// Specify the total time in seconds that is available to
// present. If this is set to a nonzero value, the pacing
// timer will work out the time available for each slide,
// instead of using the defaultTiming value
totalTime: 0,
// Specify the minimum amount of time you want to allot to
// each slide, if using the totalTime calculation method. If
// the automated time allocation causes slide pacing to fall
// below this threshold, then you will see an alert in the
// speaker notes window
minimumTimePerSlide: 0,
// Enable slide navigation via mouse wheel
mouseWheel: false,
// Hide cursor if inactive
hideInactiveCursor: true,
// Time before the cursor is hidden (in ms)
hideCursorTime: 5000,
// Hides the address bar on mobile devices
hideAddressBar: true,
// Opens links in an iframe preview overlay
// Add `data-preview-link` and `data-preview-link="false"` to customise each link
// individually
previewLinks: false,
// Transition style (e.g., none, fade, slide, convex, concave, zoom)
transition: 'linear',
// Transition speed (e.g., default, fast, slow)
transitionSpeed: 'default',
// Transition style for full page slide backgrounds (e.g., none, fade, slide, convex, concave, zoom)
backgroundTransition: 'fade',
// Number of slides away from the current that are visible
viewDistance: 3,
// Number of slides away from the current that are visible on mobile
// devices. It is advisable to set this to a lower number than
// viewDistance in order to save resources.
mobileViewDistance: 3,
// Parallax background image (e.g., "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'")
parallaxBackgroundImage: '',
// Parallax background size in CSS syntax (e.g., "2100px 900px")
parallaxBackgroundSize: '',
// Number of pixels to move the parallax background per slide
// - Calculated automatically unless specified
// - Set to 0 to disable movement along an axis
parallaxBackgroundHorizontal: null,
parallaxBackgroundVertical: null,
// The display mode that will be used to show slides
display: 'block',
// The "normal" size of the presentation, aspect ratio will be preserved
// when the presentation is scaled to fit different resolutions. Can be
// specified using percentage units.
width: 960,
height: 700,
// Factor of the display size that should remain empty around the content
margin: 0.1,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 1.5,
// PDF Export Options
// Put each fragment on a separate page
pdfSeparateFragments: true,
// For slides that do not fit on a page, max number of pages
pdfMaxPagesPerSlide: 1,
// Optional libraries used to extend on reveal.js
dependencies: [
{ src: 'reveal.js-3.9.2/plugin/zoom/zoom.js', async: true, callback: function () { Reveal.registerPlugin(RevealZoom) } },
{ src: 'reveal.js-3.9.2/plugin/notes/notes.js', async: true, callback: function () { Reveal.registerPlugin(RevealNotes) } }
],
});</script><script>var dom = {};
dom.slides = document.querySelector('.reveal .slides');
function getRemainingHeight(element, slideElement, height) {
height = height || 0;
if (element) {
var newHeight, oldHeight = element.style.height;
// Change the .stretch element height to 0 in order find the height of all
// the other elements
element.style.height = '0px';
// In Overview mode, the parent (.slide) height is set of 700px.
// Restore it temporarily to its natural height.
slideElement.style.height = 'auto';
newHeight = height - slideElement.offsetHeight;
// Restore the old height, just in case
element.style.height = oldHeight + 'px';
// Clear the parent (.slide) height. .removeProperty works in IE9+
slideElement.style.removeProperty('height');
return newHeight;
}
return height;
}
function layoutSlideContents(width, height) {
// Handle sizing of elements with the 'stretch' class
toArray(dom.slides.querySelectorAll('section .stretch')).forEach(function (element) {
// Determine how much vertical space we can use
var limit = 5; // hard limit
var parent = element.parentNode;
while (parent.nodeName !== 'SECTION' && limit > 0) {
parent = parent.parentNode;
limit--;
}
if (limit === 0) {
// unable to find parent, aborting!
return;
}
var remainingHeight = getRemainingHeight(element, parent, height);
// Consider the aspect ratio of media elements
if (/(img|video)/gi.test(element.nodeName)) {
var nw = element.naturalWidth || element.videoWidth, nh = element.naturalHeight || element.videoHeight;
var es = Math.min(width / nw, remainingHeight / nh);
element.style.width = (nw * es) + 'px';
element.style.height = (nh * es) + 'px';
} else {
element.style.width = width + 'px';
element.style.height = remainingHeight + 'px';
}
});
}
function toArray(o) {
return Array.prototype.slice.call(o);
}
Reveal.addEventListener('slidechanged', function () {
layoutSlideContents(960, 700)
});
Reveal.addEventListener('ready', function () {
layoutSlideContents(960, 700)
});
Reveal.addEventListener('resize', function () {
layoutSlideContents(960, 700)
});</script><link rel="stylesheet" href="reveal.js-3.9.2/plugin/highlight/monokai.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.3/highlight.min.js"></script>
<script>
/* highlightjs-line-numbers.js 2.6.0 | (C) 2018 Yauheni Pakala | MIT License | github.com/wcoder/highlightjs-line-numbers.js */
/* Edited by Hakim for reveal.js; removed async timeout */
!function(n,e){"use strict";function t(){var n=e.createElement("style");n.type="text/css",n.innerHTML=g(".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}",[v,L,b]),e.getElementsByTagName("head")[0].appendChild(n)}function r(t){"interactive"===e.readyState||"complete"===e.readyState?i(t):n.addEventListener("DOMContentLoaded",function(){i(t)})}function i(t){try{var r=e.querySelectorAll("code.hljs,code.nohighlight");for(var i in r)r.hasOwnProperty(i)&&l(r[i],t)}catch(o){n.console.error("LineNumbers error: ",o)}}function l(n,e){"object"==typeof n&&f(function(){n.innerHTML=s(n,e)})}function o(n,e){if("string"==typeof n){var t=document.createElement("code");return t.innerHTML=n,s(t,e)}}function s(n,e){e=e||{singleLine:!1};var t=e.singleLine?0:1;return c(n),a(n.innerHTML,t)}function a(n,e){var t=u(n);if(""===t[t.length-1].trim()&&t.pop(),t.length>e){for(var r="",i=0,l=t.length;i<l;i++)r+=g('<tr><td class="{0}"><div class="{1} {2}" {3}="{5}"></div></td><td class="{4}"><div class="{1}">{6}</div></td></tr>',[j,m,L,b,p,i+1,t[i].length>0?t[i]:" "]);return g('<table class="{0}">{1}</table>',[v,r])}return n}function c(n){var e=n.childNodes;for(var t in e)if(e.hasOwnProperty(t)){var r=e[t];h(r.textContent)>0&&(r.childNodes.length>0?c(r):d(r.parentNode))}}function d(n){var e=n.className;if(/hljs-/.test(e)){for(var t=u(n.innerHTML),r=0,i="";r<t.length;r++){var l=t[r].length>0?t[r]:" ";i+=g('<span class="{0}">{1}</span>\n',[e,l])}n.innerHTML=i.trim()}}function u(n){return 0===n.length?[]:n.split(y)}function h(n){return(n.trim().match(y)||[]).length}function f(e){e()}function g(n,e){return n.replace(/{(\d+)}/g,function(n,t){return e[t]?e[t]:n})}var v="hljs-ln",m="hljs-ln-line",p="hljs-ln-code",j="hljs-ln-numbers",L="hljs-ln-n",b="data-line-number",y=/\r\n|\r|\n/g;n.hljs?(n.hljs.initLineNumbersOnLoad=r,n.hljs.lineNumbersBlock=l,n.hljs.lineNumbersValue=o,t()):n.console.error("highlight.js not detected!")}(window,document);
/**
* This reveal.js plugin is wrapper around the highlight.js
* syntax highlighting library.
*/
(function( root, factory ) {
if (typeof define === 'function' && define.amd) {
root.RevealHighlight = factory();
} else if( typeof exports === 'object' ) {
module.exports = factory();
} else {
// Browser globals (root is window)
root.RevealHighlight = factory();
}
}( this, function() {
// Function to perform a better "data-trim" on code snippets
// Will slice an indentation amount on each line of the snippet (amount based on the line having the lowest indentation length)
function betterTrim(snippetEl) {
// Helper functions
function trimLeft(val) {
// Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill
return val.replace(/^[\s\uFEFF\xA0]+/g, '');
}
function trimLineBreaks(input) {
var lines = input.split('\n');
// Trim line-breaks from the beginning
for (var i = 0; i < lines.length; i++) {
if (lines[i].trim() === '') {
lines.splice(i--, 1);
} else break;
}
// Trim line-breaks from the end
for (var i = lines.length-1; i >= 0; i--) {
if (lines[i].trim() === '') {
lines.splice(i, 1);
} else break;
}
return lines.join('\n');
}
// Main function for betterTrim()
return (function(snippetEl) {
var content = trimLineBreaks(snippetEl.innerHTML);
var lines = content.split('\n');
// Calculate the minimum amount to remove on each line start of the snippet (can be 0)
var pad = lines.reduce(function(acc, line) {
if (line.length > 0 && trimLeft(line).length > 0 && acc > line.length - trimLeft(line).length) {
return line.length - trimLeft(line).length;
}
return acc;
}, Number.POSITIVE_INFINITY);
// Slice each line with this amount
return lines.map(function(line, index) {
return line.slice(pad);
})
.join('\n');
})(snippetEl);
}
var RevealHighlight = {
HIGHLIGHT_STEP_DELIMITER: '|',
HIGHLIGHT_LINE_DELIMITER: ',',
HIGHLIGHT_LINE_RANGE_DELIMITER: '-',
init: function( reveal ) {
// Read the plugin config options and provide fallbacks
var config = Reveal.getConfig().highlight || {};
config.highlightOnLoad = typeof config.highlightOnLoad === 'boolean' ? config.highlightOnLoad : true;
config.escapeHTML = typeof config.escapeHTML === 'boolean' ? config.escapeHTML : true;
[].slice.call( reveal.getRevealElement().querySelectorAll( 'pre code' ) ).forEach( function( block ) {
block.parentNode.className = 'code-wrapper';
// Code can optionally be wrapped in script template to avoid
// HTML being parsed by the browser (i.e. when you need to
// include <, > or & in your code).
let substitute = block.querySelector( 'script[type="text/template"]' );
if( substitute ) {
// textContent handles the HTML entity escapes for us
block.textContent = substitute.innerHTML;
}
// Trim whitespace if the "data-trim" attribute is present
if( block.hasAttribute( 'data-trim' ) && typeof block.innerHTML.trim === 'function' ) {
block.innerHTML = betterTrim( block );
}
// Escape HTML tags unless the "data-noescape" attrbute is present
if( config.escapeHTML && !block.hasAttribute( 'data-noescape' )) {
block.innerHTML = block.innerHTML.replace( /</g,"&lt;").replace(/>/g, '&gt;' );
}
// Re-highlight when focus is lost (for contenteditable code)
block.addEventListener( 'focusout', function( event ) {
hljs.highlightElement( event.currentTarget );
}, false );
if( config.highlightOnLoad ) {
RevealHighlight.highlightBlock( block );
}
} );
// If we're printing to PDF, scroll the code highlights of
// all blocks in the deck into view at once
reveal.on( 'pdf-ready', function() {
[].slice.call( reveal.getRevealElement().querySelectorAll( 'pre code[data-line-numbers].current-fragment' ) ).forEach( function( block ) {
RevealHighlight.scrollHighlightedLineIntoView( block, {}, true );
} );
} );
},
/**
* Highlights a code block. If the <code> node has the
* 'data-line-numbers' attribute we also generate slide
* numbers.
*
* If the block contains multiple line highlight steps,
* we clone the block and create a fragment for each step.
*/
highlightBlock: function( block ) {
hljs.highlightElement( block );
// Don't generate line numbers for empty code blocks
if( block.innerHTML.trim().length === 0 ) return;
if( block.hasAttribute( 'data-line-numbers' ) ) {
hljs.lineNumbersBlock( block, { singleLine: true } );
var scrollState = { currentBlock: block };
// If there is at least one highlight step, generate
// fragments
var highlightSteps = RevealHighlight.deserializeHighlightSteps( block.getAttribute( 'data-line-numbers' ) );
if( highlightSteps.length > 1 ) {
// If the original code block has a fragment-index,
// each clone should follow in an incremental sequence
var fragmentIndex = parseInt( block.getAttribute( 'data-fragment-index' ), 10 );
if( typeof fragmentIndex !== 'number' || isNaN( fragmentIndex ) ) {
fragmentIndex = null;
}
// Generate fragments for all steps except the original block
highlightSteps.slice(1).forEach( function( highlight ) {
var fragmentBlock = block.cloneNode( true );
fragmentBlock.setAttribute( 'data-line-numbers', RevealHighlight.serializeHighlightSteps( [ highlight ] ) );
fragmentBlock.classList.add( 'fragment' );
block.parentNode.appendChild( fragmentBlock );
RevealHighlight.highlightLines( fragmentBlock );
if( typeof fragmentIndex === 'number' ) {
fragmentBlock.setAttribute( 'data-fragment-index', fragmentIndex );
fragmentIndex += 1;
}
else {
fragmentBlock.removeAttribute( 'data-fragment-index' );
}
// Scroll highlights into view as we step through them
fragmentBlock.addEventListener( 'visible', RevealHighlight.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock, scrollState ) );
fragmentBlock.addEventListener( 'hidden', RevealHighlight.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock.previousSibling, scrollState ) );
} );
block.removeAttribute( 'data-fragment-index' )
block.setAttribute( 'data-line-numbers', RevealHighlight.serializeHighlightSteps( [ highlightSteps[0] ] ) );
}
// Scroll the first highlight into view when the slide
// becomes visible. Note supported in IE11 since it lacks
// support for Element.closest.
var slide = typeof block.closest === 'function' ? block.closest( 'section:not(.stack)' ) : null;
if( slide ) {
var scrollFirstHighlightIntoView = function() {
RevealHighlight.scrollHighlightedLineIntoView( block, scrollState, true );
slide.removeEventListener( 'visible', scrollFirstHighlightIntoView );
}
slide.addEventListener( 'visible', scrollFirstHighlightIntoView );
}
RevealHighlight.highlightLines( block );
}
},
/**
* Animates scrolling to the first highlighted line
* in the given code block.
*/
scrollHighlightedLineIntoView: function( block, scrollState, skipAnimation ) {
cancelAnimationFrame( scrollState.animationFrameID );
// Match the scroll position of the currently visible
// code block
if( scrollState.currentBlock ) {
block.scrollTop = scrollState.currentBlock.scrollTop;
}
// Remember the current code block so that we can match
// its scroll position when showing/hiding fragments
scrollState.currentBlock = block;
var highlightBounds = RevealHighlight.getHighlightedLineBounds( block )
var viewportHeight = block.offsetHeight;
// Subtract padding from the viewport height
var blockStyles = getComputedStyle( block );
viewportHeight -= parseInt( blockStyles.paddingTop ) + parseInt( blockStyles.paddingBottom );
// Scroll position which centers all highlights
var startTop = block.scrollTop;
var targetTop = highlightBounds.top + ( Math.min( highlightBounds.bottom - highlightBounds.top, viewportHeight ) - viewportHeight ) / 2;
// Account for offsets in position applied to the
// <table> that holds our lines of code
var lineTable = block.querySelector( '.hljs-ln' );
if( lineTable ) targetTop += lineTable.offsetTop - parseInt( blockStyles.paddingTop );
// Make sure the scroll target is within bounds
targetTop = Math.max( Math.min( targetTop, block.scrollHeight - viewportHeight ), 0 );
if( skipAnimation === true || startTop === targetTop ) {
block.scrollTop = targetTop;
}
else {
// Don't attempt to scroll if there is no overflow
if( block.scrollHeight <= viewportHeight ) return;
var time = 0;
var animate = function() {
time = Math.min( time + 0.02, 1 );
// Update our eased scroll position
block.scrollTop = startTop + ( targetTop - startTop ) * RevealHighlight.easeInOutQuart( time );
// Keep animating unless we've reached the end
if( time < 1 ) {
scrollState.animationFrameID = requestAnimationFrame( animate );
}
};
animate();
}
},
/**
* The easing function used when scrolling.
*/
easeInOutQuart: function( t ) {
// easeInOutQuart
return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t;
},
getHighlightedLineBounds: function( block ) {
var highlightedLines = block.querySelectorAll( '.highlight-line' );
if( highlightedLines.length === 0 ) {
return { top: 0, bottom: 0 };
}
else {
var firstHighlight = highlightedLines[0];
var lastHighlight = highlightedLines[ highlightedLines.length -1 ];
return {
top: firstHighlight.offsetTop,
bottom: lastHighlight.offsetTop + lastHighlight.offsetHeight
}
}
},
/**
* Visually emphasize specific lines within a code block.
* This only works on blocks with line numbering turned on.
*
* @param {HTMLElement} block a <code> block
* @param {String} [linesToHighlight] The lines that should be
* highlighted in this format:
* "1" = highlights line 1
* "2,5" = highlights lines 2 & 5
* "2,5-7" = highlights lines 2, 5, 6 & 7
*/
highlightLines: function( block, linesToHighlight ) {
var highlightSteps = RevealHighlight.deserializeHighlightSteps( linesToHighlight || block.getAttribute( 'data-line-numbers' ) );
if( highlightSteps.length ) {
highlightSteps[0].forEach( function( highlight ) {
var elementsToHighlight = [];
// Highlight a range
if( typeof highlight.end === 'number' ) {
elementsToHighlight = [].slice.call( block.querySelectorAll( 'table tr:nth-child(n+'+highlight.start+'):nth-child(-n+'+highlight.end+')' ) );
}
// Highlight a single line
else if( typeof highlight.start === 'number' ) {
elementsToHighlight = [].slice.call( block.querySelectorAll( 'table tr:nth-child('+highlight.start+')' ) );
}
if( elementsToHighlight.length ) {
elementsToHighlight.forEach( function( lineElement ) {
lineElement.classList.add( 'highlight-line' );
} );
block.classList.add( 'has-highlights' );
}
} );
}
},
/**
* Parses and formats a user-defined string of line
* numbers to highlight.
*
* @example
* RevealHighlight.deserializeHighlightSteps( '1,2|3,5-10' )
* // [
* // [ { start: 1 }, { start: 2 } ],
* // [ { start: 3 }, { start: 5, end: 10 } ]
* // ]
*/
deserializeHighlightSteps: function( highlightSteps ) {
// Remove whitespace
highlightSteps = highlightSteps.replace( /\s/g, '' );
// Divide up our line number groups
highlightSteps = highlightSteps.split( RevealHighlight.HIGHLIGHT_STEP_DELIMITER );
return highlightSteps.map( function( highlights ) {
return highlights.split( RevealHighlight.HIGHLIGHT_LINE_DELIMITER ).map( function( highlight ) {
// Parse valid line numbers
if( /^[\d-]+$/.test( highlight ) ) {
highlight = highlight.split( RevealHighlight.HIGHLIGHT_LINE_RANGE_DELIMITER );
var lineStart = parseInt( highlight[0], 10 ),
lineEnd = parseInt( highlight[1], 10 );
if( isNaN( lineEnd ) ) {
return {
start: lineStart
};
}
else {
return {
start: lineStart,
end: lineEnd
};
}
}
// If no line numbers are provided, no code will be highlighted
else {
return {};
}
} );
} );
},
/**
* Serializes parsed line number data into a string so
* that we can store it in the DOM.
*/
serializeHighlightSteps: function( highlightSteps ) {
return highlightSteps.map( function( highlights ) {
return highlights.map( function( highlight ) {
// Line range
if( typeof highlight.end === 'number' ) {
return highlight.start + RevealHighlight.HIGHLIGHT_LINE_RANGE_DELIMITER + highlight.end;
}
// Single line
else if( typeof highlight.start === 'number' ) {
return highlight.start;
}
// All lines
else {
return '';
}
} ).join( RevealHighlight.HIGHLIGHT_LINE_DELIMITER );
} ).join( RevealHighlight.HIGHLIGHT_STEP_DELIMITER );
}
}
Reveal.registerPlugin( 'highlight', RevealHighlight );
return RevealHighlight;
}));
hljs.configure({
ignoreUnescapedHTML: true,
});
hljs.highlightAll();
</script></body></html>