From 3a5d3aa1ced98c513ae0e52794a66bf5e1cf726b Mon Sep 17 00:00:00 2001 From: bossenti Date: Wed, 22 May 2024 10:21:33 +0200 Subject: [PATCH 1/4] draft release blog post --- website-v2/blog/2024-05-27-release-095.md | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 website-v2/blog/2024-05-27-release-095.md diff --git a/website-v2/blog/2024-05-27-release-095.md b/website-v2/blog/2024-05-27-release-095.md new file mode 100644 index 000000000..935dfb1d3 --- /dev/null +++ b/website-v2/blog/2024-05-27-release-095.md @@ -0,0 +1,110 @@ +--- +title: Apache StreamPipes release 0.95.0 +author: Tim Bossenmaier +authorURL: "https://github.com/bossenti" +authorImageURL: "/img/bossenmaier.png" +--- + +**
9 minutes to read
** + +We're excited to share the release of StreamPipes 0.95.0, bringing with it a range of bug fixes and new features. +This release doesn't include many large feature drops, we have worked a lot on making small things smoother and improving StreamPipes under the hood. +Always having our target of a major 1.0 release in mind. + + +places a strong emphasis on refining the technical foundations of +StreamPipes, featuring significant changes that set the stage for our major 1.0 release. +Nevertheless, we haven't overlooked the end-user experience and have implemented several enhancements. +Let's delve into the key updates introduced in this release. + + + +The current release can be downloaded here. + +## Adapters & Pipeline Elements + +### Documentation in adapter overview + +https://github.com/apache/streampipes/pull/2872 + +### Improve deletion workflow of adapters + +Within StreamPipes 0.93.0 we introduced the possibility to [edit existing adapters](https://streampipes.apache.org/blog/2023/11/28/release-093/#editing-existing-adapters). +This release improves adapter management even more by conveniently allowing to delete existing adapters even if they are used in a pipeline. +https://github.com/apache/streampipes/issues/1936 +reference to adapter editing from last release + +### Status light in adapter overview + +https://github.com/apache/streampipes/issues/2659 + +### Enhance data lake schema management +https://github.com/apache/streampipes/issues/2252 + +### Service selection for adapters + +https://github.com/apache/streampipes/pull/2691 + +### Remove deprecated adapters + +https://github.com/apache/streampipes/issues/2128 + +### New Adapter: Open Industry 4.0 devices +https://github.com/apache/streampipes/pull/2511 + +### New Adapter(s): All PLC4X protocols supported as adapters +https://github.com/apache/streampipes/issues/2632 + +just to name a few of the newbies + +### New Processor: Static Metadata enrichment + +https://github.com/apache/streampipes/issues/2350 + +### New Processor: Datetime from String +https://github.com/apache/streampipes/issues/1865 + +### New Sink: MS Teams +https://github.com/apache/streampipes/pull/2248 +Bsp aus LinkedIn post + +## UX improvements + +### Display file types in different colors + +https://github.com/apache/streampipes/issues/2107 + +### Download button for asset & files +https://github.com/apache/streampipes/issues/2192 +https://github.com/apache/streampipes/issues/2074 + +## StreamPipes Python + +https://github.com/apache/streampipes/pull/2687 + +## Administration & Development + + +### Remove Consul entirely + +https://github.com/apache/streampipes/issues/2133 + +## New Apache StreamPipes Go-Client + +https://github.com/apache/streampipes/pull/2437 + +First release, are working on more examples and better docs - stay tuned + +## Security + +TODO: list security issues + +## Final Remarks + +We highly recommend updating to StreamPipes 0.93.0 to take advantage of these new features, bug fixes, and improvements. +For more detailed information, please refer to +the [release notes](https://github.com/apache/incubator-streampipes/blob/release/0.92.0/RELEASE_NOTES.md#0920). + +We appreciate your continued support and valuable feedback. +StreamPipes is continually evolving, and we are dedicated to providing you with a powerful and reliable platform for +your IIoT streaming data needs. From e52624ea3bbacba2aa465a08388fa7fdebf08521 Mon Sep 17 00:00:00 2001 From: Dominik Riemer Date: Thu, 13 Jun 2024 11:08:19 +0200 Subject: [PATCH 2/4] Update blog post, add 0.95.0 release --- docs/01_try-installation.md | 2 +- website-v2/blog/2024-05-27-release-095.md | 110 --------- website-v2/blog/2024-06-13-release-095.md | 215 ++++++++++++++++++ website-v2/docusaurus.config.js | 2 +- website-v2/src/pages/download.tsx | 2 +- .../data-lake-sink-configuration.png | Bin 0 -> 53624 bytes .../blog/2024-06-13/datetime-from-string.png | Bin 0 -> 39341 bytes .../blog/2024-06-13/modbus-configuration.png | Bin 0 -> 89316 bytes .../img/blog/2024-06-13/ms-teams-sink.png | Bin 0 -> 195418 bytes .../blog/2024-06-13/plc-adapter-overview.png | Bin 0 -> 101734 bytes 10 files changed, 218 insertions(+), 113 deletions(-) delete mode 100644 website-v2/blog/2024-05-27-release-095.md create mode 100644 website-v2/blog/2024-06-13-release-095.md create mode 100644 website-v2/static/img/blog/2024-06-13/data-lake-sink-configuration.png create mode 100644 website-v2/static/img/blog/2024-06-13/datetime-from-string.png create mode 100644 website-v2/static/img/blog/2024-06-13/modbus-configuration.png create mode 100644 website-v2/static/img/blog/2024-06-13/ms-teams-sink.png create mode 100644 website-v2/static/img/blog/2024-06-13/plc-adapter-overview.png diff --git a/docs/01_try-installation.md b/docs/01_try-installation.md index 7af53ab22..d856083f8 100644 --- a/docs/01_try-installation.md +++ b/docs/01_try-installation.md @@ -33,7 +33,7 @@ best experience), Firefox or Edge. ## Install StreamPipes - + ## Setup StreamPipes diff --git a/website-v2/blog/2024-05-27-release-095.md b/website-v2/blog/2024-05-27-release-095.md deleted file mode 100644 index 935dfb1d3..000000000 --- a/website-v2/blog/2024-05-27-release-095.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Apache StreamPipes release 0.95.0 -author: Tim Bossenmaier -authorURL: "https://github.com/bossenti" -authorImageURL: "/img/bossenmaier.png" ---- - -**
9 minutes to read
** - -We're excited to share the release of StreamPipes 0.95.0, bringing with it a range of bug fixes and new features. -This release doesn't include many large feature drops, we have worked a lot on making small things smoother and improving StreamPipes under the hood. -Always having our target of a major 1.0 release in mind. - - -places a strong emphasis on refining the technical foundations of -StreamPipes, featuring significant changes that set the stage for our major 1.0 release. -Nevertheless, we haven't overlooked the end-user experience and have implemented several enhancements. -Let's delve into the key updates introduced in this release. - - - -The current release can be downloaded here. - -## Adapters & Pipeline Elements - -### Documentation in adapter overview - -https://github.com/apache/streampipes/pull/2872 - -### Improve deletion workflow of adapters - -Within StreamPipes 0.93.0 we introduced the possibility to [edit existing adapters](https://streampipes.apache.org/blog/2023/11/28/release-093/#editing-existing-adapters). -This release improves adapter management even more by conveniently allowing to delete existing adapters even if they are used in a pipeline. -https://github.com/apache/streampipes/issues/1936 -reference to adapter editing from last release - -### Status light in adapter overview - -https://github.com/apache/streampipes/issues/2659 - -### Enhance data lake schema management -https://github.com/apache/streampipes/issues/2252 - -### Service selection for adapters - -https://github.com/apache/streampipes/pull/2691 - -### Remove deprecated adapters - -https://github.com/apache/streampipes/issues/2128 - -### New Adapter: Open Industry 4.0 devices -https://github.com/apache/streampipes/pull/2511 - -### New Adapter(s): All PLC4X protocols supported as adapters -https://github.com/apache/streampipes/issues/2632 - -just to name a few of the newbies - -### New Processor: Static Metadata enrichment - -https://github.com/apache/streampipes/issues/2350 - -### New Processor: Datetime from String -https://github.com/apache/streampipes/issues/1865 - -### New Sink: MS Teams -https://github.com/apache/streampipes/pull/2248 -Bsp aus LinkedIn post - -## UX improvements - -### Display file types in different colors - -https://github.com/apache/streampipes/issues/2107 - -### Download button for asset & files -https://github.com/apache/streampipes/issues/2192 -https://github.com/apache/streampipes/issues/2074 - -## StreamPipes Python - -https://github.com/apache/streampipes/pull/2687 - -## Administration & Development - - -### Remove Consul entirely - -https://github.com/apache/streampipes/issues/2133 - -## New Apache StreamPipes Go-Client - -https://github.com/apache/streampipes/pull/2437 - -First release, are working on more examples and better docs - stay tuned - -## Security - -TODO: list security issues - -## Final Remarks - -We highly recommend updating to StreamPipes 0.93.0 to take advantage of these new features, bug fixes, and improvements. -For more detailed information, please refer to -the [release notes](https://github.com/apache/incubator-streampipes/blob/release/0.92.0/RELEASE_NOTES.md#0920). - -We appreciate your continued support and valuable feedback. -StreamPipes is continually evolving, and we are dedicated to providing you with a powerful and reliable platform for -your IIoT streaming data needs. diff --git a/website-v2/blog/2024-06-13-release-095.md b/website-v2/blog/2024-06-13-release-095.md new file mode 100644 index 000000000..237f3e541 --- /dev/null +++ b/website-v2/blog/2024-06-13-release-095.md @@ -0,0 +1,215 @@ +--- +title: Apache StreamPipes release 0.95.0 +author: Dominik Riemer +authorURL: "https://github.com/dominikriemer" +authorImageURL: "/img/riemer.png" +--- + +We're excited to share the release of StreamPipes 0.95.0, bringing with it a range of bug fixes and new features. +In total, the newest release closes over 200 issues. +This release doesn't include many large feature drops, we have worked a lot on making small things smoother and improving StreamPipes under the hood. +As we continue to work on the 1.0 release of Apache StreamPipes probably later this year, this release features many improvements to the end-user experience and bug fixes. + +Let's delve into the key updates introduced in this release. + + + +The current release can be downloaded here. + +## Adapters & Pipeline Elements + +### Improve deletion workflow of adapters + +Within StreamPipes 0.93.0 we introduced the possibility to [edit existing adapters](https://streampipes.apache.org/blog/2023/11/28/release-093/#editing-existing-adapters). +This release improves adapter management by conveniently allowing to delete existing adapters even if they are used in a pipeline. +When deleting a new adapter, a dialog appears which allows users to delete all pipelines which are using the data stream produced by the adapter. + +https://github.com/apache/streampipes/issues/1936 + +### Enhance data lake schema management + +We've added a new option to the data lake sink which provides users more flexibility when writing data to the internal time-series storage. +Users can choose between the `Update Schema` and `Extend existing schema` options. +By choosing `Extend schema`, it is possible to also see "old" data in the data explorer after the schema has changed. + + + +https://github.com/apache/streampipes/issues/2252 + +### New Adapter: Open Industry 4.0 devices + +A new adapter in 0.95.0 is support for the [Open Industry 4.0](https://openindustry4.com/) specification over MQTT. +The adapter can be used to connect to a variety of devices which support the data format defined in the [Development Guidelines for Open Edge Computing](https://openindustry4.com/fileadmin/Dateien/Downloads/OEC_Development_Guideline_V1.1.1.pdf). + +For instance, the adapter makes it easy to use IO-Link sensors together with an OI4-compatible IO-Link master. + +https://github.com/apache/streampipes/pull/2511 + +### New Adapter(s): All PLC4X protocols supported as adapters + +Since our "sister project" [Apache PLC4X](https://plc4x.apache.org) has introduced a new metadata API for gathering available drivers, StreamPipes 0.95.0 now supports a much larger collection of PLC connectors. +The list of newly supported protocols include: +* Allen Bradley ETH +* BACnet/IP +* Beckhoff TwinCat ADS +* EthernetIP +* Modbus ASCII/RTU/TCP + +The already existing S7 adapter is now also available with additional configuration options. + + + +Configurations on protocol and transport level are dynamically resolved from the PLC4X API. This ensures that users now have much greater flexibility when connecting to industrial protocols. + +The screenshot below shows the advanced options panel for Modbus TCP: + + + +The new adapters should be considered a beta feature and we still recommend to use the existing Modbus and S7 adapters until the new adapters have proven their stability. +More information on the usage of these new adapters and supported features can also be found on the PLC4X website. + +https://github.com/apache/streampipes/issues/2632 + +### New Processor: Static Metadata enrichment + +We added a new processor to add static metadata (such as sensor ID or location IDs) to any data stream within a pipeline. + +https://github.com/apache/streampipes/issues/2350 + +### New Processor: Datetime from String + +The new `Datetime from String` processor allows to convert a datetime string to a timestamp value and lets users conveniently choose the desired target time zone. + + + +https://github.com/apache/streampipes/issues/1865 + +### New Sink: MS Teams + +StreamPipes 0.95.0 comes with a new data sink for Microsoft Teams, which allows to publish notifications from a pipeline to a teams channel using Webhooks. + + + +https://github.com/apache/streampipes/pull/2248 + + +### Remove deprecated adapters + +We removed several outdated adapters that were deprecated in release 0.93.0: +* CoindeskBitcoinAdapter +* GdeltAdapter +* FlicMQTTAdapter +* TISensorTag +* IexCloudAdapter +* OpenSenseMapAdapter +* WikipediaAdapter + +https://github.com/apache/streampipes/issues/2128 + +## UX improvements + +### Display file types in different colors + +Different file types in the `Files` overview are now better highlighted by a dedicated color. + +https://github.com/apache/streampipes/issues/2107 + +### Download button for asset & files + +In the asset and file overview, resources can be now directly downloaded by using a new download button. + +https://github.com/apache/streampipes/issues/2192 +https://github.com/apache/streampipes/issues/2074 + +## StreamPipes Python + +As already mentioned in our [blog post](/blog/2024/03/27/anomaly-detection-with-python-functions/), we added an example to use an existing [ONNX](https://onnx.ai/) model as part of a StreamPipes function. + +https://github.com/apache/streampipes/pull/2687 + +## Administration & Development + +### Remove Consul entirely + +Following the replacement of Consul with an internal service registration and discovery mechanism, StreamPipes 0.95.0 also removes Consul from all installation files. + +https://github.com/apache/streampipes/issues/2133 + +## New Apache StreamPipes Go-Client + +Apache StreamPipes now includes a new client library for Golang. +Our first version of the Go client interacts with the StreamPipes API and provides various ways to gather data from StreamPipes. + +Here is an example on the usage of the Go client: + +```go + + +package main + +import ( + "github.com/apache/streampipes/streampipes-client-go/streampipes" + "github.com/apache/streampipes/streampipes-client-go/streampipes/config" + "log" +) + +/* + Here are some examples of using go client, including outputting the data returned by streams. + Only supports outputting model data +*/ + +func main() { + + clientConfig := config.StreamPipesClientConfig{ + Url: "http://localhost:8030", + Credential: config.StreamPipesApiKeyCredentials{ + UserName: "", + ApiKey: "", + }, + } + + streamPipesClient, err := streampipes.NewStreamPipesClient(clientConfig) + if err != nil { + log.Fatal(err) + } + + dataSeries, err := streamPipesClient.DataLakeMeasures().GetSingleDataSeries("measureName") + if err != nil { + log.Fatal(err) + } + dataSeries.Print() + + /* + output format: + + There are 2 pieces of DataSerie in the Dataseries + The 1 DataSeries + time msg test + 2024-02-23T13:37:09.052Z go-client_test 2f4556 + 2024-02-23T13:37:26.044Z go-client_test 2f4556 + 2024-02-23T13:37:29.007Z go-client_test 2f4556 + The 2 DataSeries + time msg test + 2024-02-23T13:38:06.052Z go-client_test 2f4556 + 2024-02-23T13:38:35.044Z go-client_test 2f4556 + 2024-02-23T13:38:38.007Z go-client_test 2f4556 + + */ + +} +``` + + +https://github.com/apache/streampipes/pull/2437 + +First release, are working on more examples and better docs - stay tuned + +## Final Remarks + +We highly recommend updating to StreamPipes 0.95.0 to take advantage of these new features, bug fixes, and improvements. +For more detailed information, please refer to +the [release notes](https://github.com/apache/incubator-streampipes/blob/release/0.95.0/RELEASE_NOTES.md#0950). + +We appreciate your continued support and valuable feedback. +StreamPipes is continually evolving, and we are dedicated to providing you with a powerful and reliable platform for +your IIoT streaming data needs. diff --git a/website-v2/docusaurus.config.js b/website-v2/docusaurus.config.js index 0fbb69c95..8c3edd9b9 100644 --- a/website-v2/docusaurus.config.js +++ b/website-v2/docusaurus.config.js @@ -100,7 +100,7 @@ module.exports = { textColor: 'white', isCloseable: false, content: - 'Apache StreamPipes 0.93.0 is available! ⭐️', + 'Apache StreamPipes 0.95.0 is available! ⭐️', }, "image": "img/favicon.png", "footer": { diff --git a/website-v2/src/pages/download.tsx b/website-v2/src/pages/download.tsx index 6add02f29..a80a71f7e 100644 --- a/website-v2/src/pages/download.tsx +++ b/website-v2/src/pages/download.tsx @@ -29,7 +29,7 @@ const Downloads: FC = () => (
Installation
    - +
diff --git a/website-v2/static/img/blog/2024-06-13/data-lake-sink-configuration.png b/website-v2/static/img/blog/2024-06-13/data-lake-sink-configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..584bb297ef938056e6ff6a544ee5c478a63c380b GIT binary patch literal 53624 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 O3{RWr6`j0%>wf`M)_I}; literal 0 HcmV?d00001 diff --git a/website-v2/static/img/blog/2024-06-13/datetime-from-string.png b/website-v2/static/img/blog/2024-06-13/datetime-from-string.png new file mode 100644 index 0000000000000000000000000000000000000000..2ddc65e28979996e9cbcf4c51ba799d0a67c2a1f GIT binary patch literal 39341 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 E0Z+rgAOHXW literal 0 HcmV?d00001 diff --git a/website-v2/static/img/blog/2024-06-13/modbus-configuration.png b/website-v2/static/img/blog/2024-06-13/modbus-configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..48d50d4080363df7a4ff2bd73440a5d149e189dc GIT binary patch literal 89316 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%0AVCZrtEkkVwzQ1l);G4Kuu7T%l$ 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 z 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 literal 0 HcmV?d00001 diff --git a/website-v2/static/img/blog/2024-06-13/plc-adapter-overview.png b/website-v2/static/img/blog/2024-06-13/plc-adapter-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..11ce83388d9343085024d3dfe35dc7b4fca9c00a GIT binary patch literal 101734 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 RzJ!3EE2`Qm`4=rh{tqlHbGQHi literal 0 HcmV?d00001 From 5e5b371c6fb35c4efb247c6813373ae11087044a Mon Sep 17 00:00:00 2001 From: Dominik Riemer Date: Thu, 13 Jun 2024 11:24:02 +0200 Subject: [PATCH 3/4] Remove duplicated pipeline elements from sidebar --- website-v2/sidebars.json | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/website-v2/sidebars.json b/website-v2/sidebars.json index db414d9d6..b3eba81d6 100644 --- a/website-v2/sidebars.json +++ b/website-v2/sidebars.json @@ -53,7 +53,6 @@ "items": [ "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.counter", "pe/org.apache.streampipes.processors.filters.jvm.processor.booleanfilter", - "pe/org.apache.streampipes.processors.filters.jvm.processor.booleanfilter", "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.inverter", "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.logical", "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timer", @@ -62,7 +61,6 @@ "pe/org.apache.streampipes.processors.transformation.jvm.duration-value", "pe/org.apache.streampipes.processors.textmining.jvm.chunker", "pe/org.apache.streampipes.processors.filters.jvm.compose", - "pe/org.apache.streampipes.processors.filters.jvm.compose", "pe/org.apache.streampipes.processors.transformation.jvm.count-array", "pe/org.apache.streampipes.processors.siddhi.count", "pe/org.apache.streampipes.processors.transformation.jvm.datetime", @@ -93,25 +91,18 @@ "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timekeeping", "pe/org.apache.streampipes.processors.transformation.jvm.measurementunitconverter", "pe/org.apache.streampipes.processors.filters.jvm.enrich", - "pe/org.apache.streampipes.processors.filters.jvm.enrich", - "pe/org.apache.streampipes.processors.filters.jvm.schema", "pe/org.apache.streampipes.processors.filters.jvm.schema", "pe/org.apache.streampipes.processors.filters.jvm.movingaverage", - "pe/org.apache.streampipes.processors.filters.jvm.movingaverage", "pe/org.apache.streampipes.processors.textmining.jvm.namefinder", "pe/org.apache.streampipes.processors.transformation.jvm.processor.state.labeler.number", "pe/org.apache.streampipes.processors.transformation.jvm.round", "pe/org.apache.streampipes.processors.filters.jvm.numericalfilter", - "pe/org.apache.streampipes.processors.filters.jvm.numericalfilter", "pe/org.apache.streampipes.processors.siddhi.numericalfilter", "pe/org.apache.streampipes.processors.filters.jvm.numericaltextfilter", - "pe/org.apache.streampipes.processors.filters.jvm.numericaltextfilter", "pe/org.apache.streampipes.processors.textmining.jvm.partofspeech", "pe/org.apache.streampipes.processors.filters.jvm.project", - "pe/org.apache.streampipes.processors.filters.jvm.project", "pe/org.apache.streampipes.processor.imageclassification.qrcode", "pe/org.apache.streampipes.processors.filters.jvm.limit", - "pe/org.apache.streampipes.processors.filters.jvm.limit", "pe/org.apache.streampipes.processors.textmining.jvm.sentencedetection", "pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.edge", "pe/org.apache.streampipes.processors.transformation.jvm.split-array", @@ -121,16 +112,11 @@ "pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.timer", "pe/org.apache.streampipes.processors.transformation.jvm.processor.stringoperator.state", "pe/org.apache.streampipes.processors.filters.jvm.sdt", - "pe/org.apache.streampipes.processors.filters.jvm.sdt", - "pe/org.apache.streampipes.processors.filters.jvm.merge", "pe/org.apache.streampipes.processors.filters.jvm.merge", "pe/org.apache.streampipes.processors.transformation.jvm.taskduration", "pe/org.apache.streampipes.processors.filters.jvm.textfilter", - "pe/org.apache.streampipes.processors.filters.jvm.textfilter", - "pe/org.apache.streampipes.processors.filters.jvm.threshold", "pe/org.apache.streampipes.processors.filters.jvm.threshold", "pe/org.apache.streampipes.processors.filters.jvm.throughputmon", - "pe/org.apache.streampipes.processors.filters.jvm.throughputmon", "pe/org.apache.streampipes.processors.transformation.jvm.processor.timestampextractor", "pe/org.apache.streampipes.processors.textmining.jvm.tokenizer", "pe/org.apache.streampipes.processors.siddhi.topk", @@ -214,4 +200,4 @@ "faq-common-problems" ] } -} \ No newline at end of file +} From 1afbd3f58a077f513dc2f9d4acafc02c1c9925c5 Mon Sep 17 00:00:00 2001 From: Dominik Riemer Date: Thu, 13 Jun 2024 13:18:18 +0200 Subject: [PATCH 4/4] Create release 0.95.0 --- .../version-0.95.0/01_try-installation.md | 62 ++ .../version-0.95.0/01_try-overview.md | 134 ++++ .../version-0.95.0/01_try-tutorial.md | 20 + .../version-0.95.0/02_concepts-adapter.md | 7 + .../02_concepts-data-streams.md | 7 + .../version-0.95.0/02_concepts-glossary.md | 7 + .../version-0.95.0/02_concepts-overview.md | 46 ++ .../version-0.95.0/02_concepts-pipeline.md | 7 + .../version-0.95.0/02_introduction.md | 85 +++ .../version-0.95.0/03_use-configurations.md | 48 ++ .../version-0.95.0/03_use-connect.md | 72 +++ .../version-0.95.0/03_use-dashboard.md | 66 ++ .../version-0.95.0/03_use-data-explorer.md | 102 +++ .../03_use-install-pipeline-elements.md | 9 + .../03_use-managing-pipelines.md | 52 ++ .../version-0.95.0/03_use-notifications.md | 25 + .../version-0.95.0/03_use-pipeline-editor.md | 62 ++ .../05_deploy-choosing-the-right-flavor.md | 47 ++ .../version-0.95.0/05_deploy-docker.md | 104 +++ .../05_deploy-environment-variables.md | 88 +++ .../version-0.95.0/05_deploy-kubernetes.md | 269 ++++++++ .../version-0.95.0/05_deploy-security.md | 75 +++ .../version-0.95.0/05_deploy-use-ssl.md | 35 + .../version-0.95.0/06_extend-archetypes.md | 46 ++ .../version-0.95.0/06_extend-cli.md | 190 ++++++ .../version-0.95.0/06_extend-client.md | 204 ++++++ .../version-0.95.0/06_extend-customize-ui.md | 226 +++++++ .../06_extend-first-processor.md | 54 ++ .../06_extend-sdk-event-model.md | 141 ++++ .../version-0.95.0/06_extend-sdk-functions.md | 127 ++++ .../06_extend-sdk-migration-sd.md | 117 ++++ .../06_extend-sdk-migrations.md | 179 +++++ .../06_extend-sdk-output-strategies.md | 348 ++++++++++ .../06_extend-sdk-static-properties.md | 267 ++++++++ .../06_extend-sdk-stream-requirements.md | 181 ++++++ .../version-0.95.0/06_extend-setup.md | 50 ++ .../06_extend-tutorial-adapters.md | 612 ++++++++++++++++++ .../06_extend-tutorial-data-processors.md | 454 +++++++++++++ .../06_extend-tutorial-data-sinks.md | 272 ++++++++ .../07_technicals-architecture.md | 110 ++++ .../version-0.95.0/07_technicals-messaging.md | 65 ++ .../07_technicals-runtime-wrappers.md | 37 ++ .../07_technicals-user-guidance.md | 7 + .../version-0.95.0/08_debugging.md | 7 + .../version-0.95.0/08_monitoring.md | 7 + .../version-0.95.0/09_contribute.md | 17 + .../version-0.95.0/09_get-help.md | 25 + .../version-0.95.0/faq-common-problems.md | 73 +++ ...reampipes.connect.adapters.image.stream.md | 38 ++ ...apache.streampipes.connect.adapters.iss.md | 39 ++ ...s.connect.iiot.adapters.influxdb.stream.md | 41 ++ ...treampipes.connect.iiot.adapters.iolink.md | 90 +++ ...mpipes.connect.iiot.adapters.netio.mqtt.md | 64 ++ ...mpipes.connect.iiot.adapters.netio.rest.md | 64 ++ ...e.streampipes.connect.iiot.adapters.oi4.md | 88 +++ ...streampipes.connect.iiot.adapters.opcua.md | 76 +++ ...ipes.connect.iiot.adapters.plc4x.modbus.md | 75 +++ ...eampipes.connect.iiot.adapters.plc4x.s7.md | 96 +++ ...e.streampipes.connect.iiot.adapters.ros.md | 64 ++ ...connect.iiot.adapters.simulator.machine.md | 40 ++ ...pipes.connect.iiot.protocol.stream.file.md | 90 +++ ...pipes.connect.iiot.protocol.stream.http.md | 38 ++ ...connect.iiot.protocol.stream.httpserver.md | 51 ++ ...ipes.connect.iiot.protocol.stream.kafka.md | 38 ++ ...pipes.connect.iiot.protocol.stream.mqtt.md | 53 ++ ...pipes.connect.iiot.protocol.stream.nats.md | 69 ++ ...pes.connect.iiot.protocol.stream.pulsar.md | 38 ++ ...s.connect.iiot.protocol.stream.rocketmq.md | 38 ++ ...pes.connect.iiot.protocol.stream.tubemq.md | 54 ++ ...cation.jvm.generic-image-classification.md | 52 ++ ...r.imageclassification.jvm.image-cropper.md | 43 ++ ....imageclassification.jvm.image-enricher.md | 43 ++ ...es.processor.imageclassification.qrcode.md | 68 ++ ....processors.changedetection.jvm.welford.md | 72 +++ ...eampipes.processors.enricher.jvm.jseval.md | 55 ++ ...sors.enricher.jvm.processor.math.mathop.md | 56 ++ ...nricher.jvm.processor.math.staticmathop.md | 56 ++ ...ors.enricher.jvm.processor.trigonometry.md | 56 ++ ...pes.processors.enricher.jvm.valuechange.md | 52 ++ ...eampipes.processors.filters.jvm.compose.md | 50 ++ ...reampipes.processors.filters.jvm.enrich.md | 47 ++ ...treampipes.processors.filters.jvm.limit.md | 70 ++ ...treampipes.processors.filters.jvm.merge.md | 57 ++ ...es.processors.filters.jvm.movingaverage.md | 46 ++ ....processors.filters.jvm.numericalfilter.md | 56 ++ ...cessors.filters.jvm.numericaltextfilter.md | 67 ++ ...ors.filters.jvm.processor.booleanfilter.md | 52 ++ ...eampipes.processors.filters.jvm.project.md | 48 ++ ...reampipes.processors.filters.jvm.schema.md | 46 ++ ....streampipes.processors.filters.jvm.sdt.md | 85 +++ ...pipes.processors.filters.jvm.textfilter.md | 53 ++ ...mpipes.processors.filters.jvm.threshold.md | 56 ++ ...es.processors.filters.jvm.throughputmon.md | 56 ++ ...rs.geo.jvm.jts.processor.buffergeometry.md | 95 +++ ...ssors.geo.jvm.jts.processor.bufferpoint.md | 82 +++ ...s.processors.geo.jvm.jts.processor.epsg.md | 64 ++ ....geo.jvm.jts.processor.latlngtojtspoint.md | 73 +++ ...sors.geo.jvm.jts.processor.reprojection.md | 68 ++ ...essors.geo.jvm.jts.processor.trajectory.md | 83 +++ ...eo.jvm.jts.processor.validation.complex.md | 86 +++ ...geo.jvm.jts.processor.validation.simple.md | 80 +++ ....processor.distancecalculator.haversine.md | 61 ++ ...ssor.distancecalculator.haversinestatic.md | 74 +++ ...m.latlong.processor.geocoder.googlemaps.md | 61 ++ ...ong.processor.geocoder.googlemapsstatic.md | 62 ++ ...tlong.processor.revgeocoder.geocityname.md | 67 ++ ...o.jvm.latlong.processor.speedcalculator.md | 59 ++ ...che.streampipes.processors.siddhi.count.md | 66 ++ ....streampipes.processors.siddhi.increase.md | 65 ++ ...ampipes.processors.siddhi.listcollector.md | 51 ++ ...treampipes.processors.siddhi.listfilter.md | 53 ++ ...pipes.processors.siddhi.numericalfilter.md | 63 ++ ...ache.streampipes.processors.siddhi.topk.md | 53 ++ ...pipes.processors.textmining.jvm.chunker.md | 69 ++ ...es.processors.textmining.jvm.namefinder.md | 65 ++ ....processors.textmining.jvm.partofspeech.md | 62 ++ ...essors.textmining.jvm.sentencedetection.md | 59 ++ ...pes.processors.textmining.jvm.tokenizer.md | 59 ++ ...transformation.jvm.booloperator.counter.md | 66 ++ ...ransformation.jvm.booloperator.inverter.md | 51 ++ ...transformation.jvm.booloperator.logical.md | 42 ++ ...sformation.jvm.booloperator.timekeeping.md | 70 ++ ...s.transformation.jvm.booloperator.timer.md | 58 ++ ...essors.transformation.jvm.changed-value.md | 46 ++ ...ocessors.transformation.jvm.count-array.md | 55 ++ ...ocessors.transformation.jvm.csvmetadata.md | 77 +++ ....processors.transformation.jvm.datetime.md | 78 +++ ...ssors.transformation.jvm.duration-value.md | 51 ++ ...cessors.transformation.jvm.field-mapper.md | 74 +++ ...ocessors.transformation.jvm.fieldhasher.md | 55 ++ ...ocessors.transformation.jvm.fieldrename.md | 59 ++ ...sformation.jvm.measurementunitconverter.md | 53 ++ ...rmation.jvm.processor.booloperator.edge.md | 58 ++ ...mation.jvm.processor.booloperator.state.md | 63 ++ ...tion.jvm.processor.state.labeler.number.md | 58 ++ ...sformation.jvm.processor.staticmetadata.md | 74 +++ ...tion.jvm.processor.stringoperator.state.md | 51 ++ ...mation.jvm.processor.timestampextractor.md | 58 ++ ...pes.processors.transformation.jvm.round.md | 72 +++ ...ocessors.transformation.jvm.split-array.md | 60 ++ ...ansformation.jvm.stringoperator.counter.md | 65 ++ ...transformation.jvm.stringoperator.timer.md | 66 ++ ...cessors.transformation.jvm.taskduration.md | 50 ++ ...transformation.jvm.transform-to-boolean.md | 53 ++ ...treampipes.sinks.brokers.jvm.bufferrest.md | 58 ++ ...pache.streampipes.sinks.brokers.jvm.jms.md | 60 ++ ...che.streampipes.sinks.brokers.jvm.kafka.md | 61 ++ ...ache.streampipes.sinks.brokers.jvm.mqtt.md | 61 ++ ...ache.streampipes.sinks.brokers.jvm.nats.md | 78 +++ ...he.streampipes.sinks.brokers.jvm.pulsar.md | 63 ++ ....streampipes.sinks.brokers.jvm.rabbitmq.md | 73 +++ ...ache.streampipes.sinks.brokers.jvm.rest.md | 52 ++ ....streampipes.sinks.brokers.jvm.rocketmq.md | 59 ++ ...he.streampipes.sinks.brokers.jvm.tubemq.md | 61 ++ ...streampipes.sinks.brokers.jvm.websocket.md | 52 ++ ...pache.streampipes.sinks.databases.ditto.md | 73 +++ ...streampipes.sinks.databases.jvm.couchdb.md | 63 ++ ...treampipes.sinks.databases.jvm.influxdb.md | 85 +++ ...e.streampipes.sinks.databases.jvm.iotdb.md | 91 +++ ...e.streampipes.sinks.databases.jvm.opcua.md | 65 ++ ...eampipes.sinks.databases.jvm.postgresql.md | 73 +++ ...e.streampipes.sinks.databases.jvm.redis.md | 87 +++ ...streampipes.sinks.internal.jvm.datalake.md | 68 ++ ...ampipes.sinks.internal.jvm.notification.md | 63 ++ ...reampipes.sinks.notifications.jvm.email.md | 73 +++ ...ampipes.sinks.notifications.jvm.msteams.md | 86 +++ ...pipes.sinks.notifications.jvm.onesignal.md | 63 ++ ...reampipes.sinks.notifications.jvm.slack.md | 66 ++ ...mpipes.sinks.notifications.jvm.telegram.md | 70 ++ .../version-0.95.0/user-guide-first-steps.md | 209 ++++++ .../user-guide-for-quickstart.md | 134 ++++ .../version-0.95.0/user-guide-tour.md | 305 +++++++++ .../version-0.95.0-sidebars.json | 203 ++++++ website-v2/versions.json | 1 + 174 files changed, 13630 insertions(+) create mode 100644 website-v2/versioned_docs/version-0.95.0/01_try-installation.md create mode 100644 website-v2/versioned_docs/version-0.95.0/01_try-overview.md create mode 100644 website-v2/versioned_docs/version-0.95.0/01_try-tutorial.md create mode 100644 website-v2/versioned_docs/version-0.95.0/02_concepts-adapter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/02_concepts-data-streams.md create mode 100644 website-v2/versioned_docs/version-0.95.0/02_concepts-glossary.md create mode 100644 website-v2/versioned_docs/version-0.95.0/02_concepts-overview.md create mode 100644 website-v2/versioned_docs/version-0.95.0/02_concepts-pipeline.md create mode 100644 website-v2/versioned_docs/version-0.95.0/02_introduction.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-configurations.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-connect.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-dashboard.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-data-explorer.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-install-pipeline-elements.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-managing-pipelines.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-notifications.md create mode 100644 website-v2/versioned_docs/version-0.95.0/03_use-pipeline-editor.md create mode 100644 website-v2/versioned_docs/version-0.95.0/05_deploy-choosing-the-right-flavor.md create mode 100644 website-v2/versioned_docs/version-0.95.0/05_deploy-docker.md create mode 100644 website-v2/versioned_docs/version-0.95.0/05_deploy-environment-variables.md create mode 100644 website-v2/versioned_docs/version-0.95.0/05_deploy-kubernetes.md create mode 100644 website-v2/versioned_docs/version-0.95.0/05_deploy-security.md create mode 100644 website-v2/versioned_docs/version-0.95.0/05_deploy-use-ssl.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-archetypes.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-cli.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-client.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-customize-ui.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-first-processor.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-sdk-event-model.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-sdk-functions.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migration-sd.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migrations.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-sdk-output-strategies.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-sdk-static-properties.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-sdk-stream-requirements.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-setup.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-adapters.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-processors.md create mode 100644 website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-sinks.md create mode 100644 website-v2/versioned_docs/version-0.95.0/07_technicals-architecture.md create mode 100644 website-v2/versioned_docs/version-0.95.0/07_technicals-messaging.md create mode 100644 website-v2/versioned_docs/version-0.95.0/07_technicals-runtime-wrappers.md create mode 100644 website-v2/versioned_docs/version-0.95.0/07_technicals-user-guidance.md create mode 100644 website-v2/versioned_docs/version-0.95.0/08_debugging.md create mode 100644 website-v2/versioned_docs/version-0.95.0/08_monitoring.md create mode 100644 website-v2/versioned_docs/version-0.95.0/09_contribute.md create mode 100644 website-v2/versioned_docs/version-0.95.0/09_get-help.md create mode 100644 website-v2/versioned_docs/version-0.95.0/faq-common-problems.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.image.stream.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.iss.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.influxdb.stream.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.iolink.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.mqtt.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.rest.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.oi4.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.opcua.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.modbus.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.s7.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.ros.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.simulator.machine.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.file.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.http.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.httpserver.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.kafka.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.mqtt.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.nats.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.pulsar.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.rocketmq.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.tubemq.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-cropper.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-enricher.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.qrcode.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.changedetection.jvm.welford.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.jseval.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.mathop.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.staticmathop.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.trigonometry.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.valuechange.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.compose.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.enrich.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.limit.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.merge.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.movingaverage.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericalfilter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericaltextfilter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.processor.booleanfilter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.project.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.schema.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.sdt.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.textfilter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.threshold.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.throughputmon.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.buffergeometry.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.bufferpoint.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.epsg.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.latlngtojtspoint.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.reprojection.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.trajectory.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.complex.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.simple.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversine.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversinestatic.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemaps.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemapsstatic.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.revgeocoder.geocityname.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.speedcalculator.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.count.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.increase.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listcollector.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listfilter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.numericalfilter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.topk.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.chunker.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.namefinder.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.partofspeech.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.sentencedetection.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.tokenizer.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.counter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.inverter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.logical.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timekeeping.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timer.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.changed-value.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.count-array.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.csvmetadata.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.datetime.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.duration-value.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.field-mapper.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldhasher.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldrename.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.measurementunitconverter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.edge.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.state.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.state.labeler.number.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.staticmetadata.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.stringoperator.state.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.timestampextractor.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.round.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.split-array.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.counter.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.timer.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.taskduration.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.transform-to-boolean.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.bufferrest.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.jms.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.kafka.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.mqtt.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.nats.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.pulsar.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rabbitmq.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rest.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rocketmq.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.tubemq.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.websocket.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.ditto.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.couchdb.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.influxdb.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.iotdb.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.opcua.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.postgresql.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.redis.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.datalake.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.notification.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.email.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.msteams.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.onesignal.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.slack.md create mode 100644 website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.telegram.md create mode 100644 website-v2/versioned_docs/version-0.95.0/user-guide-first-steps.md create mode 100644 website-v2/versioned_docs/version-0.95.0/user-guide-for-quickstart.md create mode 100644 website-v2/versioned_docs/version-0.95.0/user-guide-tour.md create mode 100644 website-v2/versioned_sidebars/version-0.95.0-sidebars.json diff --git a/website-v2/versioned_docs/version-0.95.0/01_try-installation.md b/website-v2/versioned_docs/version-0.95.0/01_try-installation.md new file mode 100644 index 000000000..d856083f8 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/01_try-installation.md @@ -0,0 +1,62 @@ +--- +id: try-installation +title: Installation +sidebar_label: Installation +--- + +import DownloadSection from '@site/src/components/download/DownloadSection.tsx'; + +The easiest way to install StreamPipes is our Docker-based installation. For production-grade deployments, we also +recommend looking at our Kubernetes support, which is also part of the installation kit. + +## Prerequisites + +The Docker-based installation requires **Docker** and **Docker Compose** to be installed on the target machine. +Installation instructions can be found below. + +:::info Install Docker +Go to https://docs.docker.com/installation/ and follow the instructions to install Docker for your OS. Make sure +docker can be started as a non-root user (described in the installation manual, don’t forget to log out and in +again) and check that Docker is installed correctly by executing docker-run hello-world +::: + +### Supported operating systems + +The Docker-based installation supports the operating systems **Linux**, **Mac OS X** and **Windows 10 upwards**. Older windows +versions are not fully compatible with Docker. Linux VMs running under Windows might cause network problems with Docker, +therefore some manual work might be needed to make StreamPipes run properly. + +### Web Browser + +The StreamPipes application itself will be accessible through a web browser. We recommend a recent version of Chrome ( +best experience), Firefox or Edge. + +## Install StreamPipes + + + +## Setup StreamPipes + +Once you've opened the browser at the URL given above, you should see the StreamPipes application as shown below. At +initial startup, StreamPipes automatically performs an installation process. +After the installation has finished, continue by clicking on "Go to login +page", once all components are successfully configured. + +On the login page, enter your credentials, then you should be forwarded to the home page. + +Congratulations! You've successfully managed to install StreamPipes. Now we're ready to build our first pipeline! + +

+ +:::danger Errors during the installation process +In most cases, errors during the installation are due to an under-powered system. +If there is a problem with any of the components, please restart the whole system (`docker-compose +down` and eventually also delete the volumes). +Please also make sure that you've assigned enough memory available to Docker. +::: + +## Next Steps + +That's it! Have a look at the usage guide to learn how to use Apache StreamPipes. diff --git a/website-v2/versioned_docs/version-0.95.0/01_try-overview.md b/website-v2/versioned_docs/version-0.95.0/01_try-overview.md new file mode 100644 index 000000000..29a059d4a --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/01_try-overview.md @@ -0,0 +1,134 @@ +--- +id: user-guide-introduction +title: Apache StreamPipes Documentation +sidebar_label: Overview +--- + +This is the documentation of Apache StreamPipes. + +StreamPipes Overview + + +
+ +
+
+
+ 💡 Concepts +
+
+
Learn about some general concepts of StreamPipes:
+ Overview 🔗 +
+
+
+
+
+
+ 🎓 Use +
+ +
+
+
+
+
+ 📚 Pipeline Elements +
+
+
Available pipeline elements in StreamPipes:
+ Adapters 🔗, + Data Processors 🔗, + Data Sinks 🔗 +
+
+
+
+
+
+ ⚡ Deploy +
+
+
How to set up StreamPipes in test and production environments:
+ Docker 🔗, + Kubernetes 🔗, + Use SSL 🔗 +
+
+
+ +
+
+
+ 🔧 Technicals +
+
+
Learn about technical concepts behind the curtain:
+ Architecture 🔗, + User Guidance 🔗 , + Runtime Wrappers 🔗, + Messaging 🔗, + Configuration 🔗 +
+
+
+
+
+
+ 👪 Community +
+
+
Get support and learn how to contribute to StreamPipes:
+ Get Help 🔗, + Contribute 🔗 +
+
+
+
+
+
+ 🐍 StreamPipes Python +
+
+
Discover what we offer for the Python world:
+ Python Documentation 🔗 +
+
+
+
diff --git a/website-v2/versioned_docs/version-0.95.0/01_try-tutorial.md b/website-v2/versioned_docs/version-0.95.0/01_try-tutorial.md new file mode 100644 index 000000000..c13d6f1dc --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/01_try-tutorial.md @@ -0,0 +1,20 @@ +--- +id: try-tutorial +title: Interactive Tutorial +sidebar_label: Interactive Tutorial +--- + +Once you've installed StreamPipes and see the home screen, you'll see a number of modules that are part of the StreamPipes toolbox. +As a first step, you might be interested in taking the interactive tutorial that helps you create your first pipeline. +Switch to the **Pipeline Editor** and you will see a dialog that asks you for the start of the interactive tutorial: + +Tutorial Welcome Page + +Click **Start Tour** to start the tour. In this tour, you'll build a simple pipeline that monitors (simulated) live data from a water tank system. +Within the tour, perform the actions as recommended and click **Next** to trigger the next steps. Some tour steps won't require to select **Next**, but wait for you to take the recommended action. +You can cancel the tour anytime by clicking the **Exit Tour** button. + +Tutorial Welcome Page + +Now after you've built your first pipeline, you might be interested in reading about some of our core [concepts](concepts-overview) + diff --git a/website-v2/versioned_docs/version-0.95.0/02_concepts-adapter.md b/website-v2/versioned_docs/version-0.95.0/02_concepts-adapter.md new file mode 100644 index 000000000..a94c38268 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/02_concepts-adapter.md @@ -0,0 +1,7 @@ +--- +id: concepts-adapter +title: Data Adapters +sidebar_label: Data Adapters +--- + +tbd diff --git a/website-v2/versioned_docs/version-0.95.0/02_concepts-data-streams.md b/website-v2/versioned_docs/version-0.95.0/02_concepts-data-streams.md new file mode 100644 index 000000000..329f9b908 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/02_concepts-data-streams.md @@ -0,0 +1,7 @@ +--- +id: concepts-data-streams +title: Data Streams +sidebar_label: Data Streams +--- + +tbd \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/02_concepts-glossary.md b/website-v2/versioned_docs/version-0.95.0/02_concepts-glossary.md new file mode 100644 index 000000000..b401d1829 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/02_concepts-glossary.md @@ -0,0 +1,7 @@ +--- +id: concepts-glossary +title: Glossary +sidebar_label: Glossary +--- + +tbd \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/02_concepts-overview.md b/website-v2/versioned_docs/version-0.95.0/02_concepts-overview.md new file mode 100644 index 000000000..f26f3cc07 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/02_concepts-overview.md @@ -0,0 +1,46 @@ +--- +id: concepts-overview +title: StreamPipes Concepts +sidebar_label: Overview +--- + +To understand how StreamPipes works, it is helpful to understand a few core concepts, which are illustrated below. +These encompass the entire data journey within StreamPipes: Starting with data collection ([adapters](#adapter)), +through data exchange ([data streams](#data-stream)) and data processing ([data processors](#data-processor) and [pipelines](#pipeline)), +to data persistence and distribution ([data sinks](#data-sink)). + +Overview of concepts + +## Adapter +An adapter connects to any external data source (e.g., OPC-UA, MQTT, S7 PLC, Modbus) and forwards the events it receives to the internal StreamPipes system. +Adapters can either be created by using a predefined adapter for a data source available in our marketplace [StreamPipes Connect](./03_use-connect.md). +An overview of all available adapters can be found under the menu bar **📚 Pipeline Elements**. +When you select one of these adapters, you can easily connect to the data source using an intuitive and convenient UI dialog (see the Connect section for more details). +Alternatively, you can define your own adapter by [using the provided Software Development Kit (SDK)](./06_extend-tutorial-adapters.md). +Creating an adapter is always the first step when you want to get data into StreamPipes and process it further. + +## Data Stream +**Data streams** are the primary source for working with events in StreamPipes. +A stream is an ordered sequence of events, where an event typically consists of one or more observation values and additional metadata. +The `structure` (or `schema` as we call it) of an event provided by a data stream is stored in StreamPipes' internal semantic schema registry. +Data streams are primarily created by adapters, but can also be created by a [StreamPipes Function](./06_extend-sdk-functions.md). + +## Data Processor +**Data processors** in StreamPipes transform one or more input streams into an output stream. +Such transformations can be simple, such as filtering based on a predefined rule, or more complex, such as applying rule-based or learning-based algorithms to the data. +Data processors can be applied to any data stream that meets the input requirements of a processor. +In addition, most processors can be configured by providing custom parameters directly in the user interface. +Processing elements define stream requirements, which are a set of minimum characteristics that an incoming event stream must provide. +Data processors can maintain state or perform stateless operations. + +## Data Sink +**Data sinks** consume event streams similar to data processors, but do not provide an output data stream. +As such, data sinks typically perform some action or trigger a visualization as a result of a stream transformation. +Similar to data processors, sinks also require the presence of specific input requirements from each bound data stream and can be customized. +StreamPipes provides several internal data sinks, for example, to generate notifications, visualize live data, or persist historical data from incoming streams. +In addition, StreamPipes provides several data sinks to forward data streams to external systems such as databases. + +## Pipeline +A pipeline in Apache StreamPipes describes the transformation process from a data stream to a data sink. +Typically, a pipeline consists of at least one data stream, zero or more data processors, and at least one data sink. +Pipelines are created graphically by users using the [Pipeline Editor](./03_use-pipeline-editor.md) and can be started and stopped at any time. diff --git a/website-v2/versioned_docs/version-0.95.0/02_concepts-pipeline.md b/website-v2/versioned_docs/version-0.95.0/02_concepts-pipeline.md new file mode 100644 index 000000000..3d2c5369b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/02_concepts-pipeline.md @@ -0,0 +1,7 @@ +--- +id: concepts-pipelines +title: Pipelines +sidebar_label: Pipelines +--- + +tbd \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/02_introduction.md b/website-v2/versioned_docs/version-0.95.0/02_introduction.md new file mode 100644 index 000000000..6dc71b384 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/02_introduction.md @@ -0,0 +1,85 @@ +--- +id: introduction +title: Introduction +sidebar_label: Introduction +--- + +## What is StreamPipes? + +Apache StreamPipes is a self-service Industrial IoT toolbox to enable non-technical users to connect, analyze and +explore IoT data streams. The main goal of StreamPipes is to help users bridging the gap between operational +technology (OT) and information technology (IT). This is achieved by providing a set of tools which help to make +industrial data accessible for downstream tasks such as data analytics and condition monitoring. +When working with industrial data and especially when building upon an open source stack for such tasks, users are often +faced with the management and integration of a variety of different tools for data connectivity, messaging & +integration, data enrichment, data storage, visualization and analytics. This results in an increasing operational +complexity and hardly manageable software stacks. + +Apache StreamPipes addresses this problem: It provides a complete toolbox with a variety of different tools to easily +gather data from OT systems such as Programmatic Logic Controllers (PLCs), industrial protocols (e.g., OPC-UA or +Modbus), IT protocols (e.g., MQTT) and others. Data is integrated in the form of live data streams. Based on connected +data, StreamPipes provides another module called the pipeline editor, which can be used to apply real-time analytics +algorithms on connected data stream. To this end, a library of pre-defined algorithms can be used. Out of the box, +StreamPipes provides more than 100 pipeline elements tailored at manufacturing data analytics. This includes simple +rule-based algorithms (e.g., flank detection, peak detection, boolean timers), as well as the possibility to integrate +more sophisticated ML-based algorithms. Finally, the pipeline editor allows to integrate with third-party systems by +using a variety of data sinks (e.g., to forward data to messaging brokers such as Apache Kafka, MQTT or RocketMQ, to +store data in databases such as PostgreSQL or Redis or to trigger notifications). Besides pipelines, an included data +explorer allows to visually analyze industrial IoT data. For this purpose, a number of visualizations are integrated +that allow non-technical users to quickly get first insights. Examples are correlations between several sensor values, +value heatmaps, distributions or time-series visualizations. Further tools include a dashboard used for real-time +monitoring, e.g., for visualizing live KPIs at shopfloor level. + +But StreamPipes is much more than just the user interface and an orchestration system for pipelines: It can be used as a +whole developer platform for Industrial IoT application. Apache StreamPipes is made for extensibility - it provides +several extension points, which allow the definition of custom algorithms, additional interfaces to third-party tools +and proprietary data sources. + +StreamPipes includes developer support for Java and Python, making it easy to integrate custom-trained machine learning +models into the data processing environment. With the built-in Python support, it is also possible to run online machine +learning methods directly on data streams gathered by StreamPipes. + +## Where does StreamPipes help? + +Being positioned in the industrial IoT domain, the overall goal of StreamPipes is to help manufacturing companies to +quickly build up an industrial IoT infrastructure and to analyse IIoT data without the need for manual programming. +Oftentimes, StreamPipes is compared to other tools in this area such as Node-RED for visually wiring of pipelines, which +is often used together with Grafana for data visualization and InfluxDB for time-series storage. The disadvantage of +such architectures is the system complexity beyond the first prototype, especially when it comes to production +deployments. Maintaining and securing multiple software instances is often a hard task requiring for substantial +development effort. In addition, implementing single-sign-on and providing a unified user experience is another hurdle. +This is where StreamPipes, as a single integrated tool with production-critical features such as access and role +management, provides many advantages. +StreamPipes has already a wide user range from the manufacturing domain. It helps users to quickly do the first steps +related to industrial analytics but can also be used for monitoring whole production facilities, analysing data streams +from multiple plants and sensors in real time using the integrated algorithm toolbox. Customization to individual use +cases is easy due to several extension points: + +* Software development kit for adapters, data processors and sinks: The functionality of StreamPipes can be extending by + using the integrated SDK. For instance, it is possible to integrate custom-tailored algorithms for proprietary sensors + or models into the toolbox. Additional algorithms and data sinks can be installed at runtime. +* Additional user interface plugins: StreamPipes allows to extend the default installation with additional UI views, + making use of a micro frontend approach. For instance, users can extend the system with custom-tailored views for a + specific machine or plant. Developers can use a platform API to communicate with the core StreamPipes instance. +* UI customization: To ensure a consistent look and feel, StreamPipes can be customized to the company’s corporate + identity. + +## How does StreamPipes technically work in a nutshell? + + +Overview StreamPipes Architecture + + +To foster extensibility, Apache StreamPipes is based on a microservice architecture as illustrated above. The main +services provided or used by StreamPipes are the a) user interface, b) the core, c) a time-series storage, d) a +publish/subscribe messaging layer and e) extensions services. Adapters are created over the user interface using an +intuitive configuration wizard and connect to the underlying source systems. Raw events coming from adapters can be +pre-processed (e.g., measurement unit conversions or datatype conversions). Afterwards, events are sent to the message +broker, which is the central backbone to provide IIoT data to internal and external applications. + +Besides adapters, extensions microservices can also integrate additional business logic in form of data processors and +data sinks. StreamPipes comes with over 100 built-in processors and sinks, covering basic use cases out-of-the-box. The StreamPipes core cares about orchestration of these pipeline elements and communicates with the user +interface. In addition, a time-series storage ensures persistence and can be used by any extensions service to write +data into the internal storage. The StreamPipes core provides a query interface to access historical data, which is, for +instance, used by the data explorer UI component. The user interface itself provides several built-in modules but can +also be extended with additional micro frontends. diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-configurations.md b/website-v2/versioned_docs/version-0.95.0/03_use-configurations.md new file mode 100644 index 000000000..4aa953fbb --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-configurations.md @@ -0,0 +1,48 @@ +--- +id: use-configurations +title: Configurations +sidebar_label: Configurations +--- + +The configuration section is an admin-only interface for system-wide settings. + +## General configuration + +General configuration + +The general configuration serves to provide basic system settings. The basic settings allow to configure the app name (which is used, e.g., for mails sent by StreamPipes). +Additionally, the externally available host and port can be set which is used by the mail system to add links to emails. + +Furthermore, self-registration and password recovery features can be activated in this view. Note that both features require a working email configuration. + +## Datalake + +Datalake configuration + +Here, stored data lake databases can be truncated or deleted. The view also gives information on the number of data points currently stored in a measurement series. + +## Email configuration + +Email configuration + +In this section, the email configuration is set. The email configuration is used to send mails to users. Most standard mail server settings are supported. The configuration can be validated by triggering a test mail that is sent to a given recipient. + +## Messaging + +Messaging configuration + +Messaging configuration is used to control parameters used for communication between pipeline elements. Individual Kafka settings can be configured, as well as the priority of selected message formats and protocols during pipeline creation. + +## Pipeline Element Configuration + +Pipeline element configuration + +Individual configurations of extensions services are available in this view. The available configurations depend on the provided configuration variables in the service definition of each extensions service. + +## Security + +Messaging configuration + +The security configuration allows to manage existing user accounts, service accounts and groups. New users can be added and roles can be assigned. + +Please also read more about security [here](05_deploy-security.md). diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-connect.md b/website-v2/versioned_docs/version-0.95.0/03_use-connect.md new file mode 100644 index 000000000..ba8146481 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-connect.md @@ -0,0 +1,72 @@ +--- +id: use-connect +title: StreamPipes Connect +sidebar_label: StreamPipes Connect +--- + +StreamPipes Connect is the module to connect external data sources with Apache StreamPipes directly from the user interface. +StreamPipes Connect offers various adapters for common communication protocols and some specific sensors. Besides connecting data, StreamPipes Connect offers ways to pre-process data without the need to build pipelines and integrates a schema guesser that listens for incoming data and recommends the recognized event schema. + +The screenshot below illustrates the data marketplace, which shown after navigating to "StreamPipes Connect" and then clicking the "New adapter" button at the top. + +StreamPipes Connect Overview + +## Connecting new data sources + +### Data Marketplace +The data marketplace shows a list of all adapters that are currently installed in Apache StreamPipes. Each adapter offers various configuration options which depend on the specifics of the adapter. +Adapters are distinguished a) by the data source concept they provide (data set or data stream) and b) the adapter type, where we distinguish between _generic adapters_, which usually implement a generic communication protocol such as MQTT or Apache Kafka or a specific sensor interface (e.g., for Netio power sockets). +Several filter options are available to find a suitable adapter. The configuration of a new adapter starts with selecting one of the available adapters, which starts an assistant that supports the adapter generation. + +### Protocol/Basic Settings +In the first step, basic configurations need to be provided. For instance, for an Apache PLC4X adapter, the IP address of the PLC needs to be provided. In this example, we provide basic settings for connecting to an Apache Kafka broker. After all values are provided, the "Next" button opens the next step. + +StreamPipes Connect Basic Settings + +### Format Specification +The next step, format generation, is only available for generic adapters which support different message formats to be sent over the corresponding protocol. Think of a message broker that is able to consume messages in both JSON format or binary format. +Currently supported formats include XML, various JSON representations, images and CSV. After a format has been selected, further format configurations can be provided (depending on the selected format) to further customize the incoming message format. + +StreamPipes Connect Format Selection + +### Schema Editor +In the next step, based on the previously provided protocol and format settings, the system will either provide the fixed/pre-defined schema of the adapter or, in case of specific adapters, will connect to the underlying system and try to listen for incoming data. After a few seconds, the schema editor will appear that provides a list of detected fields from the incoming events (the schema). + +StreamPipes Connect Schema Editor + +In the toolbar, several configuration options are available which transform the original schema: + +* **Add Nested Property**. This option allows to modify the structure of the event by creating a nested structure. The schema can be simply changed by dragging and dropping fields into the nested structure. +* **Add Static Value**. This option allows to add a field containing a static value (e.g., an identifier) to the event. +* **Add Timestamp**. This options appends the current timestamp to each incoming event, useful in case the timestamp is not provided by the origin. +* **Refresh**. Re-triggers the schema guessing. +* **Delete field**. Select one or more fields by clicking the checkbox on the right and trigger the delete button. +* **Property scope**. For each field, a property scope can be defined which is either _Measurement_, _Dimension_ or _Header_. These values are later be used in the pipeline editor to assist in configuring pipeline elements and do not have any functional consequence. +Use _Measurement_ to indicate the field measures a value (e.g., a temperature value from a sensor), use _Dimension_ for any identifier (e.g., the sensor ID) and use _Header_ for any other metadata such as timestamps. + +For each field (also called event property) of the schema, additional configuration options are available by clicking the _Edit_ button: + +* **Label**. Used to provide a human-readable label for the field, which will ease the identification of fields when building pipelines. +* **Runtime Name.** This is the identifier of the field in the underlying message representation format (e.g., the JSON key). Renaming the runtime name will trigger a so-called _transformation rule_ which renames the incoming field name to the new field name before forwarding it to StreamPipes. +* **Domain Property/Semantic Type**. To help StreamPipes better understand the value which is represented by the field, semantic type information can be given. Up from StreamPipes 0.68.0, the semantic type can be selected from a wide range of available options. Additionally, an URL can be manually provided that indicates the meaning of the value (e.g., http://schema.org/Temperature). +* **Mark as Timestamp**. Indicates that the selected value represents a timestamp. When selected, a _timestamp converter_ can be configured which will convert incoming timestamps to the UNIX timestamp. +* **Runtime Type**. Here, the data type can be changed +* **Unit**. Allows to specify the unit in which the value is measured. Once selected, you can also automatically convert the unit to a target unit, which will then be inserted into the data stream produced by the adapter (see screenshot below). + +StreamPipes Connect Unit Conversion + +Assigning a timestamp is mandatory and can be either done by adding a timestamp from the menu, or by choosing an existing field and marking it as timestamp. + +### Adapter Generation +Finally, the adapter is ready to be started. In the _Adapter Generation_ page, a name and description for the resulting data stream must be provided. +Once started, StreamPipes creates your new adapter and displays a preview of the connected data, which refreshes about once per second. +Afterwards, the newly created data stream is available in the pipeline editor for further usage. + +StreamPipes Connect Adapter Generation + +## Managing adapters + +Currently running adapters are available in the "Running adapters" section of StreamPipes Connect. Existing adapters can be stopped and deleted. Currently, there is no mechanism to edit an existing adapter or to stop the adapter without deleting it. + +### Adapter Templates +For frequently used configurations, adapter templates can be created. An adapter template is a pre-configured adapter which can be further customized by users. Created adapter templates are available in the marketplace similar to standard adapters. diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-dashboard.md b/website-v2/versioned_docs/version-0.95.0/03_use-dashboard.md new file mode 100644 index 000000000..339bf6b57 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-dashboard.md @@ -0,0 +1,66 @@ +--- +id: use-dashboard +title: Live Dashboard +sidebar_label: Live Dashboard +--- + +The live dashboard can be used to visualize live data of data streams using a set of visualizations +The entry page of the live dashboard lists all created dashboards as in the screenshot below: + +StreamPipes Dashboard Overview + +## Visualizing Data Streams + +To visualize data streams in the live dashboard, a pipeline must be created that makes use of the so-called **Data Lake** sink. +Any data stream or data processor can serve as an input of the data lake sink. Switch to the pipeline editor, create a pipeline and configure the data lake sink. The visualization name is used to identify the sink in case multiple data lake sinks are used within a single pipeline. + +## Managing Dashboards +Multiple dashboards can be created, e.g., to organize different assets in a single dashboard view. + +A new dashboard can be created by clicking the _New Dashboard_ button, which opens a dialog that requires basic dashboard settings such as the title and description of the new dashboard. +Once created, the dashboard will be shown in the overview. Here, the following dashboard actions are available: + +* **Show** opens the dashboard. +* **Window** opens the dashboard in a new window with reduced controls, e.g., without the StreamPipes navigation and toolbar. This is a useful view for standalone displays that should visualize key parameters. +* **Settings** allows to modify the basic dashboard settings. +* **Edit** opens the dashboard in edit mode, where widgets can be added to the dashboard. ++ **Delete** deletes the selected dashboard. + +## Creating Visualizations + +Visualizations can be added to each dashboard in form of widgets. To add new visualizations, switch to the dashboard in _Edit_ mode. +In edit mode, a button appears that allows to add a new visualization. + +Adding a new visualization is supported by a wizard consisting of three steps: + +StreamPipes Dashboard Pipeline Selection + +* **Select pipeline** is the first step where a pipeline is selected on which the visualization is based. In this view, all pipelines are listed that have at least one **Dashboard Sink**. In case a pipeline contains multiple data lake sinks, the visualization name is listed below the pipeline name which eases discovering of the proper visualization. +* **Select widget** is the next step where the visualization widget must be selected. StreamPipes automatically filters this list based on input requirements of widgets. For instance, image visualizations are only visible if the input data stream provides an image object. +* **Configure widget** provides widget-specific settings to configure the visualization. In most cases, colors and titles of widgets can be modified. Additionally, chart-specific settings such as axis value ranges can be configured. + +StreamPipes Dashboard Widget Configuration + +By clicking _Create_, the new widget is placed on the canvas. Size and positioning of visualizations can be flexibly changed based on the provided grid. To change the widget configuration, the _Settings_ button of each widget can be clicked to re-open the configuration dialog. + +Once created, the dashboard provides a live view of all visualizations: + +StreamPipes Live Dashboard + + +Before the dashboard is closed, make sure to click the _Save_ button to persist the updated dashboard. Changes can be discarded by clicking the _Discard_ button. + + +## Available widgets + +The following visualizations are available in the latest release: + +* Area Chart +* Gauge +* HTML page (renders HTML markup) +* Image +* Line Chart +* Raw (displays the raw JSON input for debugging purposes) +* Single Value (displays a single measurement) +* Table +* Traffic Light diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-data-explorer.md b/website-v2/versioned_docs/version-0.95.0/03_use-data-explorer.md new file mode 100644 index 000000000..f84323bf6 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-data-explorer.md @@ -0,0 +1,102 @@ +--- +id: use-data-explorer +title: Data Explorer +sidebar_label: Data Explorer +--- + +The data explorer can be used to visualize and explore data streams that are persisted by using the **Data Lake** sink. + +StreamPipes Data Explorer Overview + +It provides a canvas (i.e. a data view) where various visualizations from multiple pipelines can be placed. For each data view, you can set a date and time range for the configured visualizations. + +## Using the data explorer + +### Get the data + +In the data explorer, any pipeline that uses the so-called **Data Lake** sink can be explored in the data explorer. Switch to the pipeline editor and add the data lake sink to a data processor or stream. +The sink requires an index name as a configuration parameter, which is used as an identifier in the data explorer. + +### Data Views & Widgets + +After your data is stored in the data lake, you can switch over to the data-explorer tab to create a novel data view and the widgets of your choice. In StreamPipes, a data view organizes a set of related widgets (i.e. data visualizations or plots) and gets assigned a single date and time range. The standard date and time range consists of the last 15 minutes of the current date and time. You can select predefined ranges (e.g. day or month) or configure the exact date and time range you want to explore. + +StreamPipes Data Explorer Component + +First create and name your data view and select the edit icon to proceed. In your data view, you can now add a new widget congiguration (plus icon) to configure and create your first widget. The widget configuration consists of (i) data, where the individual data sources in the data lake are selected, the properties for the widget are chosen and filters on the data sources are defined and applied, (ii) visualization, where the type of widget is chosen and the respective configuration for the widget type is done and (iii) appearance, where general style configurations for the widget (such as background color) can be performed. + +### Data Configuration + +The data configuration is the first step to define your widget. You can add several data sources (i.e. data sinks) and need to configure each added data source individually. This gives you sufficient freedom to combine the needed information, potentially consisting of different data resolutions, filters or types of information. + +StreamPipes Data Explorer Data Configuration + +After selecting the initial data source, you can choose if the underlying data query is to be performed raw, aggregated or single. Raw queries refer to using the data as-is, where you can define a limit on the number of events to guarantee performant usage in the application. In aggregated mode, you can choose among predefined aggregation granularites (e.g. day, minute, second). + +In the next step, you can choose the fields (i.e. properties of your data source) you are interested in exploring. If you selected aggregation or single mode, you can also modify the type of aggregation to be performed on the selected property. + +You can also filter your data source by adding conjunctive conditions. + +### Visualization Configuration + +The visualization configuration is dependent on the visulization type, which needs to be selected first. The data-explorer currently supports the following types: + +#### Table + +The table view formats the selected properties in table format. + +StreamPipes Data Explorer Table + +#### Map + +The map allows to visualize and explore coordinates on the world map. The configuration requires to choose the property which comprises the coordinates, allows to choose the marker style, a zoom level as well as the tooltip content. + +StreamPipes Data Explorer Map + +#### Heatmap + +The heatmap widget visualizes data in terms of the available intensity, where higher values are interpreted as being more intense. You only need to select the property which you want to visualize. Note that it might be interesting to aggregate the data in the data configuration to get more insights in your heatmap. + +StreamPipes Data Explorer Heatmap + +#### Time Series + +The time series widget allows you to do exploration and analysis for your numerical and boolean data properties. You can easily visualize your data properties in various styles (i.e. scatter, line, scattered line, bar or symbol) and colors, and configure a second y-axis for better interpretation of varying property ranges. + +StreamPipes Data Explorer Time Series 1 + +StreamPipes Data Explorer Time Series 2 + +StreamPipes Data Explorer Time Series 3 + +#### Image + +The image widget enables to integrate and visualize your image data. + +#### Indicator + +The indiator widget lets you visualize a single numerical value as well as (optionally) the delta to another indicator. You only need to configure the respective properties. + +StreamPipes Data Explorer Indicator + +#### 2D Correlation + +The correlation plot currently supports analyzing the relationship of two properties. Once selected, you can choose between a scatter view of the plotted data points or directly extract correlations in a density chart. + +StreamPipes Data Explorer Correlation 1 + +StreamPipes Data Explorer Correlation 2 + +#### Distribution + +In the distribution widget, you can quickly get an overview of your data range and common data values. You can either choose a histrogram view, where a bar chart is used to show data the frequency of automatically extracted data ranges or a pie view, where you can also select the granularity of how your data is clustered in terms of frequency. + +StreamPipes Data Explorer Distribution 1 + +StreamPipes Data Explorer Distribution 2 + +### Appearance Configuration + +Finally, you can change the title of your created widget as well as background and text colors in the appearance configuration. + +StreamPipes Data Explorer Appearance diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-install-pipeline-elements.md b/website-v2/versioned_docs/version-0.95.0/03_use-install-pipeline-elements.md new file mode 100644 index 000000000..852693200 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-install-pipeline-elements.md @@ -0,0 +1,9 @@ +--- +id: use-install-pipeline-elements +title: Install Pipeline Elements +sidebar_label: Install Pipeline Elements +--- + +## Install Pipeline Elements + +(coming soon) diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-managing-pipelines.md b/website-v2/versioned_docs/version-0.95.0/03_use-managing-pipelines.md new file mode 100644 index 000000000..2c64b53ee --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-managing-pipelines.md @@ -0,0 +1,52 @@ +--- +id: use-managing-pipelines +title: Managing Pipelines +sidebar_label: Managing Pipelines +--- + +The pipeline view lists all created pipelines and provides several views and actions to manage the lifecycle of pipelines. + +In the entry screen, an overview of all created pipelines is shown: + +StreamPipes Pipeline Overview + +## Pipeline Actions +Within the pipeline overview, for each pipeline several actions are available: +* **Start/Stop pipeline** Starts or stops the selected pipeline. Once clicked, StreamPipes will trigger the selected action for all pipeline elements and open a success or error dialog as illustrated below. +* **Show details** opens the pipeline detail view (see below). +* **Modify pipeline** opens the pipeline in the pipeline editor, where the pipeline can be modified. Note that this button is only visible if the pipeline is not running. +* **Delete pipeline** opens a confirm dialog, which subsequently deletes the selected pipeline. + +The screenshot below shows the status of a pipeline after it has been successfully started. By clicking the _Show details_ button, more information on the status of each corresponding pipeline element microservice becomes available. In case of failures, the failure reason will be shown for each pipeline element that has failed to start. + +StreamPipes Pipeline Start Dialog + +## Organizing Pipelines into Categories +Pipelines can be organized into categories, which is a useful feature in case a larger amount of pipelines is created. +All categories will be shown as separate tabs in the pipeline overview. The same pipeline can be assigned to multiple categories. + +To add a new category or to add a new pipeline to an existing category, click the _Manage Categories_ button and configured the category and assigned pipelines in the dialog. + +## Pipeline Details +The pipeline details view can be opened by clicking the _Show details_ button in the pipeline overview panel. + +StreamPipes Pipeline Details + +### Overview +The overview section displays the graphical structure of the pipeline and provides some statistics about recent pipeline actions. Additionally, pipelines can be directly started, stopped, modified and deletes within this view. + +### Monitoring +Monitoring features will become available in version 0.68.0. + +### Errors +Monitoring of failures and logs will become available in version 0.69.0. + +### QuickEdit +The quick edit feature (only available for pipelines that are not running) is a quick and convenient way to modify some pipeline element configurations without opening the pipeline in the pipeline editor. +To use the quick edit feature, switch to the _QuickEdit_ tab, which will display the selected pipeline. + +By clicking a pipeline element from the preview canvas, available configuration options of the selected pipeline element can be modified. Note that only modifications that do not affect the pipeline structure (e.g., different output streams) can be changed. + +StreamPipes Pipeline Quick Edit + +After a configuration value was changed, make sure to click the _Update Pipeline_ button to save the changes. diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-notifications.md b/website-v2/versioned_docs/version-0.95.0/03_use-notifications.md new file mode 100644 index 000000000..b5c64ed98 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-notifications.md @@ -0,0 +1,25 @@ +--- +id: use-notifications +title: Notifications +sidebar_label: Notifications +--- + +The notification module can be used to create internal notifications. + +StreamPipes Notifications + +## Using notifications + +Any pipeline that includes the data sink **Notification** can trigger notifications that appear in the notification view. To configure a new notification, switch to the pipeline editor and append the notification sink to a data processor or data stream. +The sink requires a title and message as configuration parameters. + +### Placeholders + +The notification message can include placeholders for fields which are replaced with the actual value at runtime. + +## Managing notifications + +The notification view is split into two parts. The left sides lists all pipelines which include a notification sink. By selecting a pipeline, available notifications will be shown in the right panel. +By scrolling up, older notifications become visible. Notifications that have appeared in the detail view will be automatically marked as read, so that only new, unread notifications will appear in the left toolbar. + + diff --git a/website-v2/versioned_docs/version-0.95.0/03_use-pipeline-editor.md b/website-v2/versioned_docs/version-0.95.0/03_use-pipeline-editor.md new file mode 100644 index 000000000..f09cf8486 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/03_use-pipeline-editor.md @@ -0,0 +1,62 @@ +--- +id: use-pipeline-editor +title: Pipeline Editor +sidebar_label: Pipeline Editor +--- + +The pipeline editor module supports building pipelines that transform a data stream using a set of resuable data processors and data sinks. +The empty pipeline editor looks similar to the illustration below after a new installation. + +StreamPipes Pipeline Editor Overview + +## Pipeline Elements +The four main concepts data sets, data streams, data processors and data sinks are available at the top of the pipeline editor. By switching the tabs, the individual pipeline elements for each category can be found. +By clicking the questionmark symbol, which appears when hovering over an element, additional information can be viewed (e.g., for data streams a live preview of incoming data and the documentation of the pipeline element for data processors and sinks). + +StreamPipes Pipeline Element Info + +## Creating Pipelines +Pipelines are built by dragging data streams, processors and sinks into the pipeline assembly area. Typically, a pipeline is built step-by-step starting with a data soure (stream or set). +Afterwards, data processors and sinks are subsequently added to the pipeline. Connections between pipeline elements are made by selecting the gray connector of the source and moving it to the target pipeline element. +Once a connection is made, StreamPipes performs a quick validation step and, in case two pipeline elements are compatible, automatically opens a configuration window. + +### Configuring Pipeline Elements +The configuration depends on the selected pipeline element and looks similar to the screenshot below. +In general, pipeline elements are configured by providing the required values. Once the pipeline element is fully configured, the _Save_ button activates and can be used to save the configuration for the pipeline element. + +StreamPipes Pipeline Element Configuration + +In addition, the following options are available in the pipeline element configuration menu: +* **Show documentation** extends the view and displays the pipeline element's documentation next to the configuration view. +* **Show only recommended settings** filters the list of available fields provided by the connected input data stream based on the _property scope_, e.g., so that only measurement values are displayed and dimension fields from the input stream are not available for selection. If deactivated, selections contain the full list of available fields that match the input requirement of the data processor. + +### Pipeline Element Options +Further options for a pipeline element can be displayed by hovering over a pipeline element in the assembly area, so that additional buttons appear around the pipeline element: + +* **Configure element** re-opens the configuration view to update the pipeline element configuration (only available for data processors and sinks) +* **Delete element** removes the pipeline element from the pipeline +* **Help** opens the pipeline element's documentation +* **Compatible element** opens a dialog which shows all pipeline elements that are compatible to the current element's output data stream. The dialog offers an alternative to selecting pipeline elements directly from the pipeline element selection in the top. +* **Pipeline Element Recommendation** opens a dialog which shows all recommended pipeline elements that are compatible the current element's output data stream. The recommendation is based on previously connected pipeline elements and is displayed below. + +### Pipeline Editor Options +Several pipeline editor options are available in the menu bar of the pipeline assembly: + +StreamPipes Pipeline Editor Options + +* **Save pipeline** opens the save dialog (see below) +* **Pan** allows to pan within the assembly area, useful for larger pipelines that do not fit in the screen +* **Select** is visible if pan mode is active and switches back to the default select mode +* **Zoom in/out** triggers the zoom in the pipeline assembly +* **Auto Layout** layouts the pipeline in a much more beautiful way than you are able to do by yourself ;-) +* **All pipeline modification saved** is displayed if the current pipeline has been cached. Cache updates are triggered after every change of the pipeline so that changes are not lost after reloading the window. +* **Hints** are shown to display current errors (e.g., incomplete pipelines). Details can be opened by clicking the hint button. +* **Clear assembly** clears the assembly and removes the current pipeline. + +### Saving a pipeline +To save a pipeline, press the _save pipeline_ button. A dialog pops up where a name and description of the pipeline can be entered (only name is mandatory). +Additionally, a pipeline can be directly started after it has been stored by checking the corresponding button. + +StreamPipes Save Pipeline Dialog + + diff --git a/website-v2/versioned_docs/version-0.95.0/05_deploy-choosing-the-right-flavor.md b/website-v2/versioned_docs/version-0.95.0/05_deploy-choosing-the-right-flavor.md new file mode 100644 index 000000000..a140bf46e --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/05_deploy-choosing-the-right-flavor.md @@ -0,0 +1,47 @@ +--- +id: choosing-the-right-flavor +title: Choosing the right flavor +sidebar_label: Service selection options +--- + + +## Introduction + +StreamPipes comes with many different options to customize a deployment. This section introduces the various options you can choose from when installing StreamPipes. + +You can choose between various **deployment modes**, choose from two different core packages and several extension packages, wich are described below. + +## Deployment Mode + +For the deployment model, you choose between a standard multi-container `Docker-Compose` installation and the `Kubernetes` installation. +we provide several `Docker-Compose` files for the various options shown here and a `helm chart`. +See [Docker Deployment](05_deploy-docker.md) and [Kubernetes Deployment](05_deploy-kubernetes.md) for more details. + +### Running StreamPipes in a non-containerized environment + +Of course, it is also possible to launch StreamPipes in a non-containerized environment. +You will need to build your own executable binaries by running `mvn package`. +In addition, it is required to install the required 3rd party services (see [Architecture](07_technicals-architecture.md)) and configure the environment variables as described in [Environment Variables](05_deploy-environment-variables.md). + +## Core Service + +We provide two different pre-packaged versions of core services. The default `streampipes-service-core` is a packaged JAR file which includes client libraries for the various messaging systems StreamPipes supports at the cost of a larger file size. +In case you plan to run StreamPipes on less resource-intensive hardware, we recommend to switch to the `streampipes-service-core-minimal` package, which only includes support for MQTT and NATS, but has a smaller file size and slightly improved startup performance. + +## Extension Services + +Similar to the core, we provide several pre-packaged extension services which differ mainly by their file size, number of supported adapters and pipeline elements and messaging systems. + +The following packages exist: + +* `streampipes-extensions-all-jvm` is the largest package and includes all official StreamPipes adapters and pipeline elements. It also includes support for all messaging systems Streampipes currently supports. +* `streampipes-extensions-all-iiot` is a subset of the aforementioned package and excludes adapters and pipeline elements which are often not relevant for IIoT use cases. For instance, the package excludes text mining-related pipeline elements. +* `streampipes-extensions-iiot-minimal` is a subset of the aforementioned package and includes only support for the lightweight messaging systems MQTT and NATS. + +Generally said, in cases where you plan to deploy StreamPipes on a resource-limited edge device, we recommend a combination of the `streampipes-service-core-minimal` and `streampipes-extensions-iiot-minimal` package. This could, for instance, be a device with less than 4GB memory. +In other cases, it depends on the use case and if you need all adapters and pipeline elements or are ok with the IIoT-related extensions. + +## Messaging System + +StreamPipes can be configured to use different messaging systems for exchanging events between adapters and pipeline elements. +The section [Messaging](07_technicals-messaging.md) includes detailed information on the configuration of messaging systems. diff --git a/website-v2/versioned_docs/version-0.95.0/05_deploy-docker.md b/website-v2/versioned_docs/version-0.95.0/05_deploy-docker.md new file mode 100644 index 000000000..881e16b43 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/05_deploy-docker.md @@ -0,0 +1,104 @@ +--- +id: deploy-docker +title: Docker Deployment +sidebar_label: Docker Deployment +--- + +StreamPipes Compose is a simple collection of user-friendly `docker-compose` files that easily lets gain first-hand experience with Apache StreamPipes. + +> **NOTE**: We recommend StreamPipes Compose to only use for initial try-out and testing. If you are a developer and +> want to develop new pipeline elements or core feature, use the [StreamPipes CLI](06_extend-cli.md). + +#### TL;DR: A one-liner to rule them all :-) + +```bash +docker-compose up -d +``` +Go to http://localhost to finish the installation in the browser. Once finished, switch to the pipeline editor and start the interactive tour or check the [online tour](https://streampipes.apache.org/docs/docs/user-guide-tour/) to learn how to create your first pipeline! + +## Prerequisites +* Docker >= 17.06.0 +* Docker-Compose >= 1.17.0 (Compose file format: 3.4) +* Google Chrome (recommended), Mozilla Firefox, Microsoft Edge + +Tested on: **macOS, Linux, Windows 10 upwards** (CMD, PowerShell, GitBash) + +**macOS** and **Windows** users can easily get Docker and Docker-Compose on their systems by installing **Docker for Mac/Windows** (recommended). + +> **NOTE**: On purpose, we disabled all port mappings except of http port **80** to access the StreamPipes UI to provide minimal surface for conflicting ports. + +## Usage +We provide several options to get you going: + +- **default**: Default docker-compose file, called `docker-compose.yml`. +- **nats**: The standard installation which uses Nats as message broker,called `docker-compose.nats.yml`. +- **full**: Contains experimental Flink wrappers, called `docker-compose.full.yml`. +- **quickstart**: Contains predefined example assets, called `docker-compose.quickstart.yml`. The Quickstart mode is a user-friendly feature which comes with predefined example assets like pipelines, dashboards, and data views. These ready-to-use components allow first-time users to get a feel of StreamPipes in IIoT with ease, serving as a practical demonstration of how StreamPipes can be utilized for efficient monitoring and analysis. We highly recommend first-time users to begin with the Quickstart mode to understand the simplicity and convenience that StreamPipes brings to the IIoT platform. Please follow the [User Guide for Quickstart Mode](user-guide-for-quickstart.md) if you want to explore it. + + +:::info + +Other options include configurations for the internally used message broker. The current default is `Kafka`, but you can also start StreamPipes with `Nats`, `MQTT` or `Apache Pulsar`. +Use one of the other provided docker-compose files. + +::: + +**Starting** the **default** option is as easy as simply running: +> **NOTE**: Starting might take a while since `docker-compose up` also initially pulls all Docker images from Dockerhub. + +```bash +docker-compose up -d +# go to `http://localhost` after all services are started +``` +After all containers are successfully started just got to your browser and visit http://localhost to finish the installation. Once finished, switch to the pipeline editor and start the interactive tour or check the [online tour](https://streampipes.apache.org/docs/docs/user-guide-tour/) to learn how to create your first pipeline! + +**Stopping** the **default** option is similarly easy: +```bash +docker-compose down +# if you want to remove mapped data volumes, run: +# docker-compose down -v +``` + +Starting the **nats** option is almost the same, just specify the `docker-compose.nats.yml` file: +```bash +docker-compose -f docker-compose.nats.yml up -d +# go to `http://localhost` after all services are started +``` +**Stopping** the **nats** option: +```bash +docker-compose -f docker-compose.nats.yml down +``` + + +Starting the **full** option is almost the same, just specify the `docker-compose.full.yml` file: +```bash +docker-compose -f docker-compose.full.yml up -d +#go to `http://localhost` after all services are started +``` +Stopping the **full** option: +```bash +docker-compose -f docker-compose.nats.yml down +#docker-compose -f docker-compose.nats.yml down -v +``` +Starting the **quickstart** option: +```bash +docker-compose -f docker-compose.quickstart.yml build script-runner +docker-compose -f docker-compose.quickstart.yml up -d +#go to `http://localhost` after all services are started +``` +Stopping the **quickstart** option: +```bash +docker-compose -f docker-compose.quickstart.yml down +``` + +## Update services +To actively pull the latest available Docker images use: +```bash +docker-compose pull +``` + +## Upgrade +To upgrade to another StreamPipes version, simply edit the `SP_VERSION` in the `.env` file. +``` +SP_VERSION= +``` diff --git a/website-v2/versioned_docs/version-0.95.0/05_deploy-environment-variables.md b/website-v2/versioned_docs/version-0.95.0/05_deploy-environment-variables.md new file mode 100644 index 000000000..c4066fb70 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/05_deploy-environment-variables.md @@ -0,0 +1,88 @@ +--- +id: deploy-environment-variables +title: Environment Variables +sidebar_label: Environment Variables +--- + +## Introduction + +A StreamPipes installation can be configured in many ways by providing environment variables. +The following lists describe available environment variables along with a description. + +## StreamPipes Core Service + +### Internal + +| Env Variable Name | Default Value | Description | +|--------------------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------| +| SP_DEBUG | false | Should only be set for local development to reroute traffic to localhost | +| SP_INITIAL_ADMIN_EMAIL | admin@streampipes.apache.org | Installation-time variable for defining the default user name | +| SP_INITIAL_ADMIN_PASSWORD | admin | Installation-time variable for defining the default user password | +| SP_INITIAL_SERVICE_USER | sp-service-client | Installation-time variable for defining the initial service user (must be same to the configured user in the extension service) | +| SP_INITIAL_SERVICE_USER_SECRET | my-apache-streampipes-secret-key-change-me | Installation-time variable for defining the initial service secret (minimum 35 chars) | +| SP_JWT_SECRET | Empty for Docker, Auto-generated for K8s | JWT secret, base64-encoded, minimum 256 bits | +| SP_JWT_SIGNING_MODE | HMAC | HMAC or RSA, RSA can be used to authenticate Core-Extensions communication | +| SP_JWT_PRIVATE_KEY_LOC | Empty | Required id SP_JWT_SIGNING_MODE=RSA, path to the private key, can be generated in the UI (Settings->Security->Generate Key Pair) | +| SP_ENCRYPTION_PASSCODE | eGgemyGBoILAu3xckolp for Docker, Auto-generated for K8s | Encryption passcode for `SecretStaticProperties` | +| SP_PRIORITIZED_PROTOCOL | kafka | Messaging layer for data exchange between extensions | + + +### Third-party services + +| Env Variable Name | Default Value | Description | +|------------------------|---------------|---------------------------------------------------------------------------| +| SP_COUCHDB_HOST | couchdb | The hostname or IP of the CouchDB database | +| SP_COUCHDB_PROTOCOL | http | The protocol (http or https) of the CouchDB database | +| SP_COUCHDB_PORT | 5984 | The port of the CouchDB database | +| SP_COUCHDB_USER | admin | The user of the CouchDB database (must have permissions to add databases) | +| SP_COUCHDB_PASSWORD | admin | The password of the CouchDB user | +| SP_TS_STORAGE_HOST | influxdb | The hostname of the timeseries storage (currently InfluxDB) | +| SP_TS_STORAGE_PORT | 8086 | The port of the timeseries storage | +| SP_TS_STORAGE_PROTOCOL | http | The protocol of the timeseries storage (http or https) | +| SP_TS_STORAGE_BUCKET | sp | The InfluxDB storage bucket name | +| SP_TS_STORAGE_ORG | sp | The InfluxDB storage org | +| SP_TS_STORAGE_TOKEN | sp-admin | The InfluxDB storage token | + +The InfluxDB itself can be configured by providing the variables `DOCKER_INFLUXDB_INIT_PASSWORD` and `DOCKER_INFLUXDB_INIT_ADMIN_TOKEN`. See the `docker-compose` file for details. + +## StreamPipes Extensions Service + +### Internal + +| Env Variable Name | Default Value | Description | +|--------------------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------| +| SP_CLIENT_USER | Empty | Service account for communication with Core | +| SP_CLIENT_SECRET | Empty | Service secret for communication with Core | +| SP_EXT_AUTH_MODE | sp-service-client | When set to AUTH: all interfaces are only accessible with authentication (requires SP_JET_PRIVATE_KEY_LOC in Core) | +| SP_JWT_PUBLIC_KEY_LOC | my-apache-streampipes-secret-key-change-me | Path to the public key of the corresponding SP_JWT_PRIVATE_KEY defined in Core | + +### Third-party services + +The following variables are only required for extensions which require access to the internal time-series storage (the `Data Lake Sink`). + +| Env Variable Name | Default Value | Description | +|------------------------|---------------|---------------------------------------------------------------------------| +| SP_TS_STORAGE_HOST | influxdb | The hostname of the timeseries storage (currently InfluxDB) | +| SP_TS_STORAGE_PORT | 8086 | The port of the timeseries storage | +| SP_TS_STORAGE_PROTOCOL | http | The protocol of the timeseries storage (http or https) | +| SP_TS_STORAGE_BUCKET | sp | The InfluxDB storage bucket name | +| SP_TS_STORAGE_ORG | sp | The InfluxDB storage org | +| SP_TS_STORAGE_TOKEN | sp-admin | The InfluxDB storage token | + + +## Recommended variables + +For a standard deployment, it is recommended to customize the following variables: + +* Initiales Admin-Passwort (SP_INITIAL_ADMIN_PASSWORD, Core) +* Initiales Client Secret (SP_INITIAL_SERVICE_USER_SECRET, Core) +* Client Secret Extensions (SP_CLIENT_USER, Extensions) +* Encryption Passcode (SP_ENCRYPTION_PASSCODE, Core) +* CouchDB-Password (SP_COUCHDB_PASSWORD, Core + Extensions + CouchDB) +* InfluxDB Storage Password (DOCKER_INFLUXDB_INIT_PASSWORD, InfluxDB) +* InfluxDB Storage Token (SP_TS_STORAGE_TOKEN (Core, Extensions) + * DOCKER_INFLUXDB_INIT_ADMIN_TOKEN (InfluxDB service) + +## Auto-generation of variables in K8s setups + +See the [Kubernetes Guide](05_deploy-kubernetes.md) for an overview of auto-generated variables. diff --git a/website-v2/versioned_docs/version-0.95.0/05_deploy-kubernetes.md b/website-v2/versioned_docs/version-0.95.0/05_deploy-kubernetes.md new file mode 100644 index 000000000..6734676b4 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/05_deploy-kubernetes.md @@ -0,0 +1,269 @@ +--- +id: deploy-kubernetes +title: Kubernetes Deployment +sidebar_label: Kubernetes Deployment +--- + +## Prerequisites +Requires Helm (https://helm.sh/) and an actively running Kubernetes cluster. + +## Usage +We provide helm chart options to get you going in the `installer/k8s`folder. + +**Starting** the default helm chart option is as easy as simply running the following command from the root of this folder: +> **NOTE**: Starting might take a while since we also initially pull all Docker images from Dockerhub. + +```bash +helm install streampipes ./ +``` +After a while, all containers should successfully started, indicated by the `Running` status. + +The `values.yaml` file contains several configuration options to customize your StreamPipes installation. See the section below for all configuration options. + +## Ingress + +The helm chart provides several options to configure an Ingress or to define an Ingressroute that directly integrates with Traefik. + +## Dynamic Volume Provisioning + +You can override the `storageClassName` variable to configure StreamPipes for dynamic volume provisioning. + +## Parameters + +Here is an overview of the supported parameters to configure StreamPipes. + +### Common parameters + +| Parameter Name | Description | Value | +|--------------------------------------------------|---------------------------------------------------------|-----------------------------------------| +| deployment | Deployment type (lite or full) | lite | +| preferredBroker | Preferred broker for deployment | "nats" | +| monitoringSystem | Enable monitoring system (true/false) | false | +| pullPolicy | Image pull policy | "Always" | +| restartPolicy | Restart policy for the container | Always | +| persistentVolumeReclaimPolicy | Reclaim policy for persistent volumes | "Delete" | +| persistentVolumeAccessModes | Access mode for persistent volumes | "ReadWriteOnce" | +| initialDelaySeconds | Initial delay for liveness and readiness probes | 60 | +| periodSeconds | Interval between liveness and readiness probes | 30 | +| failureThreshold | Number of consecutive failures for readiness probes | 30 | +| hostPath | Host path for the application | "" | + +### StreamPipes common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|---------------------------------------------------------|------------------------------------------| +| streampipes.version | StreamPipes version | "0.93.0-SNAPSHOT" | +| streampipes.registry | StreamPipes registry URL | "apachestreampipes" | +| streampipes.auth.secretName | The secret name for storing secrets | "sp-secrets" | +| streampipes.auth.users.admin.user | The initial admin user | "admin@streampipes.apache.org" | +| streampipes.auth.users.admin.password | The initial admin password (leave empty for autogen) | "admin" | +| streampipes.auth.users.service.user | The initial service account user | "sp-service-client" | +| streampipes.auth.users.service.secret | The initial service account secret | empty (auto-generated) | +| streampipes.auth.encryption.passcode | Passcode for value encryption | empty (auto-generated) | +| streampipes.core.appName | StreamPipes backend application name | "backend" | +| streampipes.core.port | StreamPipes backend port | 8030 | +| streampipes.core.persistence.storageClassName | Storage class name for backend PVs | "hostpath" | +| streampipes.core.persistence.storageSize | Size of the backend PV | "1Gi" | +| streampipes.core.persistence.claimName | Name of the backend PersistentVolumeClaim | "backend-pvc" | +| streampipes.core.persistence.pvName | Name of the backend PersistentVolume | "backend-pv" | +| streampipes.core.service.name | Name of the backend service | "backend" | +| streampipes.core.service.port | TargetPort of the StreamPipes backend service | 8030 | +| streampipes.ui.appName | StreamPipes UI application name | "ui" | +| streampipes.ui.resolverActive | Flag for enabling DNS resolver for Nginx proxy | true | +| streampipes.ui.port | StreamPipes UI port | 8088 | +| streampipes.ui.resolver | DNS resolver for Nginx proxy | "kube-dns.kube-system.svc.cluster.local" | +| streampipes.ui.service.name | Name of the UI service | "ui" | +| streampipes.ui.service.type | Type of the UI service | "ClusterIP" | +| streampipes.ui.service.nodePort | Node port for the UI service | 8088 | +| streampipes.ui.service.port | TargetPort of the StreamPipes UI service | 8088 | +| streampipes.ingress.active | Flag for enabling Ingress for StreamPipes | false | +| streampipes.ingress.annotations | Annotations for Ingress | {} | +| streampipes.ingress.host | Hostname for Ingress | "" | +| streampipes.ingressroute.active | Flag for enabling IngressRoute for StreamPipes | true | +| streampipes.ingressroute.annotations | Annotations for IngressRoute | {} | +| streampipes.ingressroute.entryPoints | Entry points for IngressRoute | ["web", "websecure"] | +| streampipes.ingressroute.host | Hostname for IngressRoute | "" | +| streampipes.ingressroute.certResolverActive | Flag for enabling certificate resolver for IngressRoute | true | +| streampipes.ingressroute.certResolver | Certificate resolver for IngressRoute | "" | + + +### Extensions common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|---------------------------------------------------------|------------------------------------------| +| extensions.iiot.appName | IIoT extensions application name | extensions-all-iiot | +| extensions.iiot.port | Port for the IIoT extensions application | 8090 | +| extensions.iiot.service.name | Name of the IIoT extensions service | extensions-all-iiot | +| extensions.iiot.service.port | TargetPort of the IIoT extensions service | 8090 | + + +### External common parameters + +#### Couchdb common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| external.couchdb.appName | CouchDB application name | "couchdb" | +| external.couchdb.version | CouchDB version | 3.3.1 | +| external.couchdb.user | CouchDB admin username | "admin" | +| external.couchdb.password | CouchDB admin password | empty (auto-generated) | +| external.couchdb.port | Port for the CouchDB service | 5984 | +| external.couchdb.service.name | Name of the CouchDB service | "couchdb" | +| external.couchdb.service.port | TargetPort of the CouchDB service | 5984 | +| external.couchdb.persistence.storageClassName | Storage class name for CouchDB PVs | "hostpath" | +| external.couchdb.persistence.storageSize | Size of the CouchDB PV | "1Gi" | +| external.couchdb.persistence.claimName | Name of the CouchDB PersistentVolumeClaim | "couchdb-pvc" | +| external.couchdb.persistence.pvName | Name of the CouchDB PersistentVolume | "couchdb-pv" | + +#### Influxdb common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| external.influxdb.appName | InfluxDB application name | "influxdb" | +| external.influxdb.version | InfluxDB version | 2.6 | +| external.influxdb.username | InfluxDB admin username | "admin" | +| external.influxdb.password | InfluxDB admin password | empty (auto-generated) | +| external.influxdb.adminToken | InfluxDB admin token | empty (auto-generated) | +| external.influxdb.initOrg | InfluxDB initial organization | "sp" | +| external.influxdb.initBucket | InfluxDB initial bucket | "sp" | +| external.influxdb.initMode | InfluxDB initialization mode | "setup" | +| external.influxdb.apiPort | Port number for the InfluxDB service (API) | 8083 | +| external.influxdb.httpPort | Port number for the InfluxDB service (HTTP) | 8086 | +| external.influxdb.grpcPort | Port number for the InfluxDB service (gRPC) | 8090 | +| external.influxdb.service.name | Name of the InfluxDB service | "influxdb" | +| external.influxdb.service.apiPort | TargetPort of the InfluxDB service for API | 8083 | +| external.influxdb.service.httpPort | TargetPort of the InfluxDB service for HTTP | 8086 | +| external.influxdb.service.grpcPort | TargetPort of the InfluxDB service for gRPC | 8090 | +| external.influxdb.persistence.storageClassName | Storage class name for InfluxDB PVs | "hostpath" | +| external.influxdb.persistence.storageSize | Size of the InfluxDB PV | "1Gi" | +| external.influxdb.persistence.storageSizeV1 | Size of the InfluxDB PV for v1 databases | "1Gi" | +| external.influxdb.persistence.claimName | Name of the InfluxDBv2 PersistentVolumeClaim | "influxdb2-pvc" | +| external.influxdb.persistence.claimNameV1 | Name of the InfluxDBv1 PersistentVolumeClaim | "influxdb-pvc" | +| external.influxdb.persistence.pvName | Name of the InfluxDBv2 PersistentVolume | "influxdb2-pv" | +| external.influxdb.persistence.pvNameV1 | Name of the InfluxDBv1 PersistentVolume | "influxdb-pv" | + + +#### Nats common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| external.nats.appName | NATS application name | "nats" | +| external.nats.port | Port for the NATS service | 4222 | +| external.nats.version | NATS version | | +| external.nats.service.type | Type of the NATS service | "NodePort" | +| external.nats.service.externalTrafficPolicy | External traffic policy for the NATS service | "Local" | +| external.nats.service.name | Name of the NATS service | "nats" | +| external.nats.service.port | TargetPort of the NATS service | 4222 | + + +#### Kafka common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| external.kafka.appName | Kafka application name | "kafka" | +| external.kafka.version | Kafka version | 2.2.0 | +| external.kafka.port | Port for the Kafka service | 9092 | +| external.kafka.external.hostname | Name which will be advertised to external clients. Clients which use (default) port 9094 | "localhost" +| external.kafka.service.name | Name of the Kafka service | "kafka" | +| external.kafka.service.port | TargetPort of the Kafka service | 9092 | +| external.kafka.service.portOutside | Port for Kafka client outside of the cluster | 9094 | +| external.kafka.persistence.storageClassName | Storage class name for Kafka PVs | "hostpath" | +| external.kafka.persistence.storageSize | Size of the Kafka PV | "1Gi" | +| external.kafka.persistence.claimName | Name of the Kafka PersistentVolumeClaim | "kafka-pvc" | +| external.kafka.persistence.pvName | Name of the Kafka PersistentVolume | "kafka-pv" | +| + +#### Zookeeper common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| external.zookeeper.appName | ZooKeeper application name | "zookeeper" | +| external.zookeeper.version | ZooKeeper version | 3.4.13 | +| external.zookeeper.port | Port for the ZooKeeper service | 2181 | +| external.zookeeper.service.name | Name of the ZooKeeper service | "zookeeper" | +| external.zookeeper.service.port | TargetPort of the ZooKeeper service | 2181 | +| external.zookeeper.persistence.storageClassName | Storage class name for ZooKeeper PVs | "hostpath" | +| external.zookeeper.persistence.storageSize | Size of the ZooKeeper PV | "1Gi" | +| external.zookeeper.persistence.claimName | Name of the ZooKeeper PersistentVolumeClaim | "zookeeper-pvc" | +| external.zookeeper.persistence.pvName | Name of the ZooKeeper PersistentVolume | "zookeeper-pv" | + + +#### Pulsar common parameters + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| external.pulsar.appName | pulsar application name | "pulsar" | +| external.pulsar.version | pulsar version | 3.0.0 | +| external.pulsar.port | Port for the pulsar service | 6650 | +| external.pulsar.service.name | Name of the pulsar service | "pulsar" | +| external.pulsar.service.port | TargetPort of the pulsar service | 6650 | +| external.pulsar.persistence.storageClassName | Storage class name for pulsar PVs | "hostpath" | +| external.pulsar.persistence.storageSize | Size of the pulsar PV | "1Gi" | +| external.pulsar.persistence.claimName | Name of the pulsar PersistentVolumeClaim | "pulsar-pvc" | +| external.pulsar.persistence.pvName | Name of the pulsar PersistentVolume | "pulsar-pv" | + +### Monitoring common parameters + +#### Monitoring - Prometheus + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| prometheus.appName | Prometheus application name | "prometheus" | +| prometheus.version | Prometheus version | 2.45.0 | +| prometheus.port | Prometheus port | 9090 | +| prometheus.service.name | Prometheus service name | "prometheus" | +| prometheus.service.port | Prometheus service port | 9090 | +| prometheus.persistence.storageClassName | Prometheus storage class name | "hostpath" | +| prometheus.persistence.storageSize | Prometheus storage size | "2Gi" | +| prometheus.persistence.claimName | Prometheus PVC claim name | "prometheus-pvc" | +| prometheus.persistence.pvName | Prometheus PV name | "prometheus-pv" | +| prometheus.persistence.tokenStorageSize | Prometheus token storage size | "16Ki" | +| prometheus.config.scrapeInterval | Prometheus scrape interval | 10s | +| prometheus.config.evaluationInterval | Prometheus evaluation interval | 15s | +| prometheus.config.backendJobName | Prometheus backend job name | "backend" | +| prometheus.config.extensionsName | Prometheus extensions job name | "extensions-all-iiot" | +| prometheus.config.tokenFileName | Prometheus token file name | "token" | +| prometheus.config.tokenFileDir | Prometheus token file directory | "/opt/data" + +#### Monitoring - Grafana + +| Parameter Name | Description | Value | +|-------------------------------------------------|----------------------------------------------------------|------------------------------------------| +| grafana.appName | Grafana application name | "grafana" | +| grafana.version | Grafana version | 10.1.2 | +| grafana.port | Grafana port | 3000 | +| grafana.service.name | Grafana service name | "grafana" | +| grafana.service.port | Grafana service port | 3000 | +| grafana.persistence.storageClassName | Grafana storage class name | "hostpath" | +| grafana.persistence.storageSize | Grafana storage size | "1Gi" | +| grafana.persistence.claimName | Grafana PVC claim name | "grafana-pvc" | +| grafana.persistence.pvName | Grafana PV name | "grafana-pv" | + + +## Auto-generation of parameters. + +The helm chart includes a `secrets.yaml` file which auto-generates several settings as follows: + +```yaml + +apiVersion: v1 +kind: Secret +metadata: + name: sp-secrets + namespace: {{ .Release.Namespace | quote }} +type: Opaque +data: + sp-initial-admin-password: {{ ternary (randAlphaNum 10) .Values.streampipes.auth.users.admin.password (empty .Values.streampipes.auth.users.admin.password) | b64enc | quote }} + sp-initial-client-secret: {{ ternary (randAlphaNum 35) .Values.streampipes.auth.users.service.secret (empty .Values.streampipes.auth.users.service.secret) | b64enc | quote }} + sp-encryption-passcode: {{ ternary (randAlphaNum 20) .Values.streampipes.auth.encryption.passcode (empty .Values.streampipes.auth.encryption.passcode) | b64enc | quote }} + sp-couchdb-password: {{ ternary (randAlphaNum 20) .Values.external.couchdb.password (empty .Values.external.couchdb.password) | b64enc | quote }} + sp-ts-storage-password: {{ ternary (randAlphaNum 20) .Values.external.influxdb.password (empty .Values.external.influxdb.password) | b64enc | quote }} + sp-ts-storage-token: {{ ternary (randAlphaNum 20) .Values.external.influxdb.adminToken (empty .Values.external.influxdb.adminToken) | b64enc | quote }} + +``` + + +## Deleting the current helm chart deployment: +```bash +helm uninstall streampipes +``` diff --git a/website-v2/versioned_docs/version-0.95.0/05_deploy-security.md b/website-v2/versioned_docs/version-0.95.0/05_deploy-security.md new file mode 100644 index 000000000..cae5bdcbf --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/05_deploy-security.md @@ -0,0 +1,75 @@ +--- +id: deploy-security +title: Security +sidebar_label: Security +--- + +## Overriding default settings + +At installation time, StreamPipes checks for available environment variables relevant for the securing the system. If they are not set, it will use the default values. + +The following variables are checked by the core at installation time: + +* SP_INITIAL_ADMIN_EMAIL The email address of the initial administrator. +* SP_INITIAL_ADMIN_PASSWORD The password of the initial administrator. +* SP_INITIAL_CLIENT_USER The initial client user, used by the extensions modules to make authenticated API requests to the core. +* SP_INITIAL_CLIENT_SECRET The default password of the initial client user. +* SP_SETUP_INSTALL_PIPELINE_ELEMENTS Indicates whether pipeline elements should be installed. +* SP_ENCRYPTION_PASSCODE The encryption passcode, used for securely storing secrets (e.g., database connection strings). +* SP_JWT_SECRET The JWT secret, used for signing JWT tokens. + +In addition, all extensions services that perform requests to the core will need to have the following environment variables set: + +* SP_CLIENT_USER The client user, used by the extensions modules to make authenticated API requests to the core. +* SP_CLIENT_SECRET The password of the client user. + +Note that there are default values for all environment variables that are set at installation time - make sure to change these settings when moving to production! + +## Configuration + +Most security-related settings can be set in the configuration section of StreamPipes. The *General* section allows to set self-service registration and password recovery (both are disabled by default and require a valid email configuration). +In the *Security* section, users, service accounts, roles and groups can be configured. + + +## User types + +StreamPipes distinguishes between User Accounts (real users that interact with StreamPipes over the UI or an API) and Service Accounts (user-independent accounts which solely use StreamPipes over the API). + +User accounts are typically used by extensions service that require API access to the core (e.g., to get a list of running pipelines). + +## Permissions + +StreamPipes v0.69.0 comes with more advanced mechanisms to manage permissions. +For each major resource (pipeline elements, pipelines, StreamPipes Connect adapters, dashboards, data explorer views), permissions can be assigned individually to users and groups. + +To ease permission handling, StreamPipes comes with a default number of roles with pre-assigned privileges: + +### Roles + +* Admin The administrator role has full access to all resources. +* Service Admin The service administrator role has full access to all resources, but has no access to the UI. +* Pipeline Admin has full control of pipelines (create, edit, delete, start, stop, pause, resume, etc.). +* Pipeline User has limited control of pipelines (read only). +* Dashboard Admin has full control of dashboards (create, edit, delete, etc.). +* Dashboard User has limited control of dashboards (read only). +* Data Explorer Admin has full control of data explorer views (create, edit, delete, etc.). +* Data Explorer User has limited control of data explorer views (read only). +* Connect Admin has full control of StreamPipes Connect adapters (create, edit, delete, etc.). + +### Groups + +Roles can be either assigned to specific users or groups. Any group can contain several members. +The permissions of a user are the union of the permissions of all roles assigned to the user and the groups to which the user belongs. + +### Changing permissions + +Any resource has a resource owner, which is the authority that created the resource. Resources can be either public or private. Public resources are available to all users, while the user role determines what the user can do with the resource. +E.g., a public pipeline created by a user of role ROLE_ADMIN can be edited by all users with role PIPELINE_ADMIN, while the same pipeline can be read by all users with role PIPELINE_USER. + +Permissions can only be changed by admin users currently. +In the overview section of each resource (e.g., pipelines and dashboards), a permission dialog is available to users with role ROLE_ADMIN. The dialog allows to assign users and groups to the individual resource. + + + + + diff --git a/website-v2/versioned_docs/version-0.95.0/05_deploy-use-ssl.md b/website-v2/versioned_docs/version-0.95.0/05_deploy-use-ssl.md new file mode 100644 index 000000000..d5762b8dc --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/05_deploy-use-ssl.md @@ -0,0 +1,35 @@ +--- +id: deploy-use-ssl +title: Use SSL +sidebar_label: Use SSL +--- + +This page explains how SSL Certificates can be used to provide transport layer security between your Browser and the Streampipes Backend. + +## Prerequisites +You need a valid Certificate consisting of a Private and a Public Key. Both Keys must be in PEM Format. Please note that your Private Key should never be shared, otherwise the communication can not be considered secure. + +## Edit docker-compose.yml +In order to use SSL you have to open port 443 on the nginx Service. Incoming insecure Traffic on Port 80 will be automatically rerouted to Port 443. + +The Environment-Variable NGINX_SSL must be set to "true". + +Finally you have to inject the Certificates into the Docker-Container. In the example below, the Certificates are placed in the directory /etc/ssl/private/ on the host machine. Please change the path according to the place where the Certificates are located on your machine. The path after the colon should not be changed! +```yaml +[...] + nginx: + image: apachestreampipes/ui + ports: + - "80:80" + - "443:443" + environment: + - NGINX_SSL=true + volumes: + - /etc/ssl/private/private.pem:/etc/nginx/ssl/ssl.pem + - /etc/ssl/private/public.pem:/etc/nginx/ssl/cert.pem + depends_on: + - backend + networks: + spnet: +[...] +``` diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-archetypes.md b/website-v2/versioned_docs/version-0.95.0/06_extend-archetypes.md new file mode 100644 index 000000000..a6907f0ee --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-archetypes.md @@ -0,0 +1,46 @@ +--- +id: extend-archetypes +title: Maven Archetypes +sidebar_label: Maven Archetypes +--- + +In this tutorial we explain how you can use the Maven archetypes to develop your own StreamPipes processors and sinks. +We use IntelliJ in this tutorial, but it works with any IDE of your choice. + +## Prerequisites +You need to have Maven installed, further you need an up and running StreamPipes installation on your development computer. + +## Create Project +To create a new project, we provide multiple Maven Archteypes. +Currently, we provide archetypes for standalone Java-based microservices and archetypes for the experimental Flink wrapper. +The commands required to create a new pipeline element project can be found below. Make sure that you select a version compatible with your StreamPipes installation. +Copy the command into your terminal to create a new project. +The project will be created in the current folder. +First, the ``groupId`` of the resulting Maven artifact must be set. +We use ``groupId``: ``org.example`` and ``artifactId``: ``ExampleProcessor``. +You can keep the default values for the other settings, confirm them by hitting enter. + +:::info Choosing the right version +Make sure that the version used to create your archetype matches your running Apache StreamPipes version. +In the example below, replace `{sp.version}` with the proper version, e.g., `0.92.0`. +::: + +```bash +mvn archetype:generate \ + -DarchetypeGroupId=org.apache.streampipes \ + -DarchetypeArtifactId=streampipes-archetype-extensions-jvm \ + -DarchetypeVersion={sp.version} +``` + + +## Project structure +Open the project in your IDE. +If everything worked, the structure should look similar to the following image. +In the *main* package, it is defined which processors / sinks you want to activate and the *pe.example* package contains two skeletons for creating a data processor and sink. +For details, have a look at the other parts of the Developer Guide, where these classes are explained in more depth. + +Project Structure + +## Next steps + +Click [here](06_extend-first-processor.md) to learn how to create your first data processor. diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-cli.md b/website-v2/versioned_docs/version-0.95.0/06_extend-cli.md new file mode 100644 index 000000000..e5f93cfd6 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-cli.md @@ -0,0 +1,190 @@ +--- +id: extend-cli +title: StreamPipes CLI +sidebar_label: StreamPipes CLI +--- + +The StreamPipes command-line interface (CLI) is focused on developers in order to provide an easy entrypoint to set up a suitable dev environment, either planning on developing + +* new extensions such as **connect adapters, processors, sinks** or, +* new core features for **backend** and **ui**. + +The main difference between the standard Docker/K8s installation is an improved communication between services running as containers and services running locally for development. + +The CLI can be found in the [main repository](https://github.com/apache/streampipes/tree/master/installer/cli) or in the ``installer/cli`` folder of the downloaded source code. + +## TL;DR + +```bash +streampipes env --list +[INFO] Available StreamPipes environment templates: +pipeline-element +... +streampipes env --set pipeline-element +streampipes up -d +``` +> **NOTE**: use `./installer/cli/streampipes` if you haven't add it to the PATH and sourced it (see section "Run `streampipes` from anywhere?"). + +## Prerequisites +The CLI is basically a wrapper around multiple `docker` and `docker-compose` commands plus some additional sugar. + +* Docker >= 17.06.0 +* Docker-Compose >= 1.26.0 (Compose file format: 3.4) +* Google Chrome (recommended), Mozilla Firefox, Microsoft Edge +* For Windows Developer: GitBash only + + +Tested on: (***macOS**, **Linux**, **Windows***) + +> **NOTE**: If you're using Windows the CLI only works in combination with GitBash - CMD, PowerShell won't work. + + +## CLI commands overview + +``` +StreamPipes CLI - Manage your StreamPipes environment with ease + +Usage: streampipes COMMAND [OPTIONS] + +Options: + --help, -h show help + --version, -v show version + +Commands: + clean Remove StreamPipes data volumes, dangling images and network + down Stop and remove StreamPipes containers + env Inspect and select StreamPipes environments + info Get information + logs Get container logs for specific container + ps List all StreamPipes container for running environment + pull Download latest images from Dockerhub + restart Restart StreamPipes environment + up Create and start StreamPipes container environment + +Run 'streampipes COMMAND --help' for more info on a command. +``` + +## Usage: Along dev life-cycle + +**List** available environment templates. +```bash +streampipes env --list +``` + +**Inspect** services in an available environment to know what kind of services it is composed of. +```bash +streampipes env --inspect pipeline-element +``` + +**Set** environment, e.g. `pipeline-element`, if you want to write a new pipeline element. +```bash +streampipes env --set pipeline-element +``` + +**Start** environment ( default: `dev` mode). Here the service definition in the selected environment is used to start the multi-container landscape. +> **NOTE**: `dev` mode is enabled by default since we rely on open ports to core service such as `couchdb`, `kafka` etc. to reach from the IDE when developing. If you don't want to map ports (except the UI port), then use the `--no-ports` flag. + +```bash +streampipes up -d +# start in production mode with unmapped ports +# streampipes up -d --no-ports +``` +Now you're good to go to write your new pipeline element :tada: :tada: :tada: + +> **HINT for extensions**: Use our [Maven archetypes](https://streampipes.apache.org/docs/docs/extend-archetypes/) to set up a project skeleton and use your IDE of choice for development. However, we do recommend using IntelliJ. + +> **HINT for core**: To work on `backend` or `ui` features you need to set the template to `backend` and clone the core repository [streampipes](https://github.com/apache/streampipes) - check the prerequisites there for more information. + +**Stop** environment and remove docker container +```bash +streampipes down +# want to also clean docker data volumes when stopping the environment? +# streampipes down -v +``` + +## Additionally, useful commands + +**Start individual services only?** We got you! You chose a template that suits your needs and now you only want to start individual services from it, e.g. only Kafka and InfluxDB. + +> **NOTE**: the service names need to be present and match your current `.spenv` environment. + +```bash +streampipes up -d kafka influxdb +``` + +**Get current environment** (if previously set using `streampipes env --set `). +```bash +streampipes env +``` + +**Get logs** of specific service and use optional `--follow` flag to stay attached to the logs. +```bash +streampipes logs --follow backend +``` + +**Update** all services of current environment +```bash +streampipes pull +``` + +**Restart** all services of current environment or specific services +```bash +streampipes restart +# restart backend +# streampipes restart backend +``` + +**Clean** your system and remove created StreamPipes Docker volumes, StreamPipes docker network and dangling StreamPipes images of old image layers. +```bash +streampipes clean +# remove volumes, network and dangling images +# streampipes clean --volumes +``` + +## Modify/Create an environment template +As of now, this step has to be done **manually**. All environments are located in `environments/`. + +```bash +├── adapter # developing a new connect adapter +├── backend # developing core backend features +├── basic # wanna run core, UI, connect etc from the IDE? +├── full # full version containing more pipeline elements +├── lite # few pipeline elements, less memory +├── pipeline-element # developing new pipeline-elements +└── ui # developing UI features +``` +**Modifying an existing environment template**. To modify an existing template, you can simply add a `` to the template. +> **NOTE**: You need to make sure, that the service your are adding exists in `deploy/standalone/service/`. If your're adding a completely new service take a look at existing ones, create a new service directory and include a `docker-compose.yml` and `docker-compose.dev.yml` file. + +``` +[environment:backend] +activemq +kafka +... + +``` + +**Creating a new** environment template. To create a new environment template, place a new file `environments/` in the template directory. Open the file and use the following schema. +> **IMPORTANT**: Please make sure to have `[environment:]` header in the first line of your new template matching the name of the file. Make sure to use small caps letters (lowercase) only. + +``` +[environment:] + + +... +``` + +## Run `streampipes` from anywhere? No problem +Simply add the path to this cli directory to your `$PATH` (on macOS, Linux) variable, e.g. in your `.bashrc` or `.zshrc`, or `%PATH%` (on Windows). + +For **macOS**, or **Linux**: + +```bash +export PATH="/path/to/streampipes-installer/installer/cli:$PATH" +``` + +For **Windows** add `installer\cli` to environment variables, e.g. check this [documentation](https://helpdeskgeek.com/windows-10/add-windows-path-environment-variable/). + + +## Upgrade to new version +To upgrade to a new version, simply edit the version tag `SP_VERSION` in the `.env` file. diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-client.md b/website-v2/versioned_docs/version-0.95.0/06_extend-client.md new file mode 100644 index 000000000..f584c3d2c --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-client.md @@ -0,0 +1,204 @@ +--- +id: extend-client +title: StreamPipes Client +sidebar_label: StreamPipes Client +--- + + +:::info Looking for Python support? + +This section explains how to use the Apache StreamPipes Java Client. Please read the Python docs to find out how to use +the client for Python. + +::: + +## About the StreamPipes client + +Sometimes you don't want to write your own extensions to StreamPipes, but want to interact with StreamPipes from +external application. +One example is to influence the lifecycle of pipelines - think of a feature which automatically starts or stops specific +pipelines that monitor the production of a specific product. + +Another example is to gather live data from Apache StreamPipes, e.g., to consume data that has been previously connected +by an external, standalone application. + +For such use cases, we provide the StreamPipes client, which is currently available in Python and Java. This section +covers the usage of the Java client. + +## Using the StreamPipes client + +:::info Choosing the right version + +Your client library version should match the installed Apache StreamPipes version. Replace `${streampipes.version}` with +the version of your installation, e.g., `0.92.0`. + +::: + +In your Java project, add the following dependency to your pom file: + +```xml + + + org.apache.streampipes + streampipes-client + ${streampipes.version} + + +``` + +## Obtaining an API token + +Overview StreamPipes Architecture + +To communicate with Apache StreamPipes, you need to provide proper credentials. There are two ways to obtain +credentials: + +* An API token, which is bound to a user. The API token can be generate from the UI clicking on the user icon and then + navigate to `Profile/API`. +* A service user, which can be created by users with role `Admin`. + +Service users can have their own permissions, while API tokens inherit all permissions from the corresponding user. + +## Connecting to StreamPipes + +Once you have your API token and configured your dependencies, you can connect to an Apache StreamPipes instance as +follows: + +```java + +CredentialsProvider credentials=StreamPipesCredentials + .withApiKey("admin@streampipes.apache.org","YOUR_API_KEY"); + +// Create an instance of the StreamPipes client + StreamPipesClient client=StreamPipesClient + .create("localhost",8082,credentials,true); + +``` + +The following configurations are required: + +* The `withApiKey` method expects the username and the API key. Alternatively, use the `withServiceToken` method to + authenticate as a service user. +* The client instance requires the hostname or IP address of your running StreamPipes instance. In addition, you need to + provide the port, the credentials object and a flag which needs to be set in case the StreamPipes instance is not + served over HTTPS. +* There are short-hand convenience options to create a client instance. + +## Working with the client + +Here are some examples how you can work with the StreamPipes client: + +```java + +// Get streams +List streams=client.streams().all(); + +// Get a specific stream + Optional stream=client.streams().get("STREAM_ID"); + +// see the schema of a data stream + EventSchema schema=stream.get().getEventSchema(); + +// print the list of fields of this stream + List fields=schema.getEventProperties(); + +// Get all pipelines + List pipelines=client.pipelines().all(); + +// Start a pipeline + PipelineOperationStatus status=client.pipelines().start(pipelines.get(0)); + +// Stop a pipeline with providing a pipeline Id + PipelineOperationStatus status=client.pipelines().stop("PIPELINE_ID"); + +// Get all pipeline element templates + List templates=client.pipelineElementTemplates().all(); + +// Get all data sinks + List dataSinks=client.sinks().all(); + + +``` + +## Consuming live data + +StreamPipes supports a variety of messaging protocols to internally handle data streams. If you plan to gather live data +from the client library, you also need to add one or more of the supported messaging +protocols to the pom file. The default protocol depends on the StreamPipes configuration and is set in the `.env` file +in your installation folder. + +```xml + + + + org.apache.streampipes + streampipes-messaging-kafka + ${streampipes.version} + + + + +org.apache.streampipes +streampipes-messaging-nats +${streampipes.version} + + + + + +org.apache.streampipes +streampipes-messaging-mqtt +${streampipes.version} + + +``` + +In addition, add the message format that is used internally by StreamPipes. The default message format used by +StreamPipes is JSON, so let's include the dependency as well: + +```xml + + + + org.apache.streampipes + streampipes-dataformat-json + ${streampipes.version} + + +``` + +Once you've imported the dependencies, it is easy to consume live data. First, register the protocols and formats in +your client instance: + +```java + +client.registerProtocol(new SpKafkaProtocolFactory()); + +// or Nats: + client.registerProtocol(new SpNatsProtocolFactory()); + +// data format: + client.registerDataFormat(new JsonDataFormatFactory()); + +``` + +Then, you are ready to consume data: + +```java + +client.streams().subscribe(dataStreams.get(0),new EventProcessor() { +@Override +public void onEvent(Event event) { + // example + MapUtils.debugPrint(System.out,"event",event.getRaw()); + } + }); + +``` + +:::tip + +There are many more options to work with the StreamPipes Client - e.g., you can trigger emails directly from the API. +Just explore the various classes and interfaces provided by the client! + +::: diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-customize-ui.md b/website-v2/versioned_docs/version-0.95.0/06_extend-customize-ui.md new file mode 100644 index 000000000..c09823ffb --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-customize-ui.md @@ -0,0 +1,226 @@ +--- +id: extend-customize-ui +title: UI customization +sidebar_label: UI customization +--- + + +## Custom theme + +It is possible to use a custom theme with individual styles, logos and images instead of the default StreamPipes theme. + +In this section, we describe the necessary steps to build and deploy a custom theme. + + +### Prerequisite: Learn how to run and build the UI + +To use a custom theme, it is required to build the UI with the custom settings. +In general, the UI can be found in the `ui` folder of the source code. + +Perform the following steps to build the UI; + +```bash + +# Install all necessary packages +npm install + +# Start the UI for development purposes +npm run start + +# Build the StreamPipes UI +npm run build + +``` + +## Customizable assets + +The following assets can be provided in a customized theme: + +* **Logo** This is the main logo image, which is shown e.g., on the login page. +* **Navigation Logo** This is the logo which appears in the top navigation bar after successful login +* **Favicon** The favicon is shown in the browser navbar. It is also used as the loading animation in StreamPipes. +* **String constants** Customizable strings, e.g., when you want to use another application name than **Apache StreamPipes**. +* **Theme variables** An scss file which defines custom colors and layouts. + +## Customize constants + +To customize constants, you can create a custom file `app.constants.ts` and modify the content based on the template below: + +```javascript + +import {Injectable} from '@angular/core'; + +@Injectable() +export class AppConstants { + + public readonly APP_NAME = "Apache StreamPipes"; + public readonly APP_TITLE = 'Apache StreamPipes'; + public readonly EMAIL = "admin@streampipes.apache.org"; +} + + +``` + +## Customize theme + +To customize the theme, we provide a file named `variables.scss` which can be overridden with default color and style settings. + +See the example below: + +```scss + +/*! + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +$sp-color-primary: rgb(57, 181, 74); +$sp-color-primary-600: #06c12a; + +$sp-color-accent: #1b1464; + +$sp-color-accent-light-blue: rgb(59, 92, 149); +$sp-color-accent-light: rgb(156, 156, 156); +$sp-color-accent-light-transparent: rgba(156, 156, 156, 0.4); + +$sp-color-accent-dark: #83a3de; + +$sp-color-adapter: #7f007f; +$sp-color-stream: #ffeb3b; +$sp-color-processor: #009688; +$sp-color-sink: #3f51b5; + +$sp-color-error: #b71c1c; + +body { + --color-data-view: rgb(122, 206, 227); + --color-dashboard: rgb(76, 115, 164); + --color-adapter: rgb(182, 140, 97); + --color-data-source: #ffeb3b; + --color-pipeline: rgb(102, 185, 114); + --color-measurement: rgb(39, 164, 155); + --color-file: rgb(163, 98, 190); + + --button-border-radius: 5px; + --iconbar-width: 35px; + --navbar-icon-border-radius: 0; + --navbar-icon-padding: 0; +} + +:root { + --color-loading-bar: #{$sp-color-accent}; +} + +.dark-mode { + --color-primary: #{$sp-color-primary}; + --color-accent: #{$sp-color-accent-dark}; + --color-bg-outer: var(--color-bg-1); + --color-bg-page-container: var(--color-bg-0); + --color-bg-main-panel-header: var(--color-bg-0); + --color-bg-main-panel-content: var(--color-bg-0); + --color-bg-navbar-icon: inherit; + --color-bg-navbar-icon-selected: inherit; + --color-bg-0: #121212; + --color-bg-1: #282828; + --color-bg-2: #404040; + --color-bg-3: #424242; + --color-bg-4: #5f5f5f; + --color-bg-dialog: rgb(66, 66, 66); + --color-shadow: #c4c4c4; + --color-pe: #404040; + --color-default-text: rgba(255, 255, 255, 0.87); + --color-warn: #b36161; + + --color-tab-border: #cccccc; + + --color-navigation-bg: var(--color-primary); + --color-navigation-link-text: var(--color-bg-0); + --color-navigation-text: #121212; + --color-navigation-selected: #{$sp-color-primary}; + --color-navigation-hover: #{$sp-color-primary-600}; + --color-navigation-bg-selected: var(--color-bg-1); + --color-navigation-divider: #{$sp-color-primary}; + + --content-box-color: #404040; + --canvas-color: linear-gradient( + 90deg, + rgba(50, 50, 50, 0.5) 10%, + transparent 0% + ), + linear-gradient(rgba(50, 50, 50, 0.5) 10%, transparent 0%); +} + +.light-mode { + --color-primary: #{$sp-color-primary}; + --color-accent: #{$sp-color-accent}; + --color-bg-outer: var(--color-bg-1); + --color-bg-page-container: var(--color-bg-0); + --color-bg-main-panel-header: var(--color-bg-0); + --color-bg-main-panel-content: var(--color-bg-0); + --color-bg-navbar-icon: inherit; + --color-bg-navbar-icon-selected: inherit; + --color-bg-0: #ffffff; + --color-bg-1: #fafafa; + --color-bg-2: #f1f1f1; + --color-bg-3: rgb(224, 224, 224); + --color-bg-4: rgb(212, 212, 212); + --color-bg-dialog: #ffffff; + --color-shadow: #555; + --color-pe: #ffffff; + --color-default-text: #121212; + --color-warn: #b71c1c; + + --color-tab-border: #cccccc; + + --color-navigation-bg: var(--color-primary); + --color-navigation-link-text: var(--color-bg-0); + --color-navigation-text: #ffffff; + --color-navigation-selected: #{$sp-color-primary}; + --color-navigation-hover: #{$sp-color-primary-600}; + --color-navigation-bg-selected: var(--color-bg-1); + --color-navigation-divider: var(--color-primary); + + --content-box-color: rgb(156, 156, 156); + --canvas-color: linear-gradient( + 90deg, + rgba(208, 208, 208, 0.5) 10%, + transparent 0% + ), + linear-gradient(rgba(208, 208, 208, 0.5) 10%, transparent 0%); +} + +``` +## Run a customized build + +To create a new UI build with customized themes, use the following command: + +````bash + +UI_LOC=PATH_TO_FOLDER_WITH_CUSTOM_TEMPLATES \\ +THEME_LOC=$UI_LOC/_variables.scss \\ +LOGO_HEADER_LOC=$UI_LOC/img/logo.png \\ +FAVICON_LOC=$UI_LOC/img/favicon.png \\ +LOGO_NAV_LOC=$UI_LOC/img/logo-navigation.png \\ +CONSTANTS_FILE=$UI_LOC/app.constants.ts \\ +npm run build + +```` + +First, we create a helper environment variable that links to a folder which includes custom logos, the theme file and constants. +Next, we set the variables above to override default logos and stylings. +Finally, the usual build process is executed. + +Once finished, you've successfully customized an Apache StreamPipes instance! diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-first-processor.md b/website-v2/versioned_docs/version-0.95.0/06_extend-first-processor.md new file mode 100644 index 000000000..96080508f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-first-processor.md @@ -0,0 +1,54 @@ +--- +id: extend-first-processor +title: Your first data processor +sidebar_label: Your first data processor +--- + +In this section, we will explain how to start a pipeline element service and install it using the StreamPipes UI. + +Open the class *ExampleDataProcessor* and edit the ``onEvent`` method to print the incoming event, log it to the console and send it to the next component without changing it. + +```java +@Override +public void onEvent(Event event, SpOutputCollector collector) { + // Print the incoming event on the console + System.out.println(event); + + // Hand the incoming event to the output collector without changing it. + collector.collect(event); +} +``` + +## Start Processor +Starting from StreamPipes 0.69.0, the IP address of an extensions service (processor, adapter or sink) will be auto-discovered upon start. +The auto-discovery is done by the StreamPipes service discovery mechanism and should work for most setups. +Once you start an extensions service, you will see the chosen IP in printed in the console. Make sure that this IP does not point to localhost (127.0.0.1). +If you see such an IP or the extensions service complains that it cannot resolve the IP, you can manually set the IP address of the extensions service. You can do so by providing an SP_HOST environment variable. + + +Project Structure + +To check if the service is up and running, open the browser on *'localhost:8090'* (or the port defined in the service definition). The machine-readable description of the processor should be visible as shown below. + + +:::caution Common Problems +If the service description is not shown on 'localhost:8090', you might have to change the port address. +This needs to be done in the configuration of your service, further explained in the configurations part of the developer guide. + +If the service does not show up in the StreamPipes installation menu, click on 'MANAGE ENDPOINTS' and add 'http://YOUR_IP_OR_DNS_NAME:8090'. +Use the IP or DNS name you provided as the SP_HOST variable or the IP (if resolvable) found by the auto-discovery service printed in the console. +After adding the endpoint, a new processor with the name *Example* should show up. +::: + +Now you can go to StreamPipes. +Your new processor *'Example'* should now show up in the installation menu ("Install Pipeline Elements" in the left navigation bar). +Install it, then switch to the pipeline view and create a simple pipeline that makes use of your newly created processor. +In case you opened the StreamPipes installation for the first time, it should have been automatically installed during the setup process. + +Project Structure + +Start this pipeline. +Now you should see logging messages in your console and, once you've created a visualization, you can also see the resulting events of your component in StreamPipes. + +Congratulations, you have just created your first processor! +From here on you can start experimenting and implement your own algorithms. diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-event-model.md b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-event-model.md new file mode 100644 index 000000000..42cc8d472 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-event-model.md @@ -0,0 +1,141 @@ +--- +id: extend-sdk-event-model +title: "SDK Guide: Event Model" +sidebar_label: "SDK: Event Model" +--- + +## Introduction + +This guide explains the usage of the event model to manipulate runtime events for data processors and data sink. + +## Prerequisites + +This guide assumes that you are already familiar with the basic setup of [data processors](06_extend-first-processor.md). + +### Property Selectors + +In most cases, fields that are subject to be transformed by pipeline elements are provided by the assigned ``MappingProperty`` (see the guide on [static properties](extend-sdk-static-properties)). + +Mapping properties return a ``PropertySelector`` that identifies a field based on (i) the **streamIndex** and (ii) the runtime name of the field. +Let's assume we have an event with the following structure: + +```json +{ + "timestamp" : 1234556, + "temperature" : 37.0, + "deviceId" : "sensor1", + "running" : true, + "location" : {"latitude" : 34.4, "longitude" : -47}, + "lastValues" : [45, 22, 21] +} +``` + +In addition, we assume that a data processor exists (with one input node) that converts the temperature value (measured in degrees celsius) to a degree fahrenheit value. +In this case, a mapping property (selected by the pipeline developer in the StreamPipes UI) would link to the ``temperature`` field of the event. + +The mapping property value will be the ``PropertySelector`` of the temperature value, which looks as follows: + +``` +s0::temperature +``` + +``s0`` identifies the stream (in this case, only one input streams exist, but as data processors might require more than one input stream, a stream identifier is required), while the appendix identifies the runtime name. + +Note: If you add a new field to an input event, you don't need to provide the selector, you can just assign the runtime name as defined by the [output strategy](extend-sdk-output-strategies). + +### Reading Fields + +You can get a field from an event by providing the corresponding selector: + +```java + +@Override + public void onEvent(Event event, SpOutputCollector out) { + + PrimitiveField temperatureField = event.getFieldBySelector(PROPERTY_SELECTOR).getAsPrimitive(); + } + +``` + +Similarly, if your mapping property links to a nested property, use + +```java + +@Override + public void onEvent(Event event, SpOutputCollector out) { + + NestedField nestedField = event.getFieldBySelector(PROPERTY_SELECTOR).getAsNested(); + } + +``` + +and for a list-based field: + +```java + +@Override + public void onEvent(Event event, SpOutputCollector out) { + + ListField listField = event.getFieldBySelector(PROPERTY_SELECTOR).getAsList(); + } + +``` + +### Parsing Fields + +#### Primitive Fields + +A ``PrimitiveField`` contains convenience methods to directly cast a field to the target datatype: + +```java + +// parse the value as a float datatype +Float temperatureValue = event.getFieldBySelector(temperatureSelector).getAsPrimitive().getAsFloat(); + +// or do the same with a double datatype +Double temperatureValue = event.getFieldBySelector(temperatureSelector).getAsPrimitive().getAsDouble(); + +// extracting a string +String deviceId = event.getFieldBySelector(deviceIdSelector).getAsPrimitive().getAsString(); + +// this also works for extracting fields from nested fields: +Double latitude = event.getFieldBySelector(latitudeSelector).getAsPrimitive().getAsDouble(); + +// extracting boolean values +Boolean running = event.getFieldBySelector(runningSelector).getAsPrimitive().getAsBoolean(); +``` + +In rare cases, you might want to receive a field directly based on the runtime name as follows: + +```java +Double temperature = event.getFieldByRuntimeName("temperature").getAsPrimitive().getAsDouble(); +``` + +#### List Fields + +Lists can also be retrieved by providing the corresponding selector and can automatically be parsed to a list of primitive datatypes: + +```java + +List lastValues = event.getFieldBySelector(lastValueSelector).getAsList().parseAsSimpleType(Integer.class); + +``` + +(coming soon: parsing complex lists) + + +### Adding/Updating Fields + +Primitive fields can easily be added to an event by providing the runtime name and the object: + +```java + + // add a primitive field with runtime name "city" and value "Karlsruhe" + event.addField("city", "Karlsruhe"); + + // remove the field "temperature" from the event + event.removeFieldBySelector(temperatureSelector); + + // add a new field + event.addField("fahrenheit", 48); +``` diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-functions.md b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-functions.md new file mode 100644 index 000000000..659690b71 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-functions.md @@ -0,0 +1,127 @@ +--- +id: extend-sdk-functions +title: "SDK Guide: Functions" +sidebar_label: "SDK: Functions" +--- + +## Introduction + +Pipeline elements such as data processors and data sinks are a great way +to create _reusable_ components that can be part of pipelines. +However, creating a pipeline element is not always the best choice: + +* The behaviour of a data processor is bound to a specific input stream _and_ +* A data processor doesn't contain any user-defined configuration _and_ +* The intended action is fixed or known at build time and the data processor shouldn't be available in the pipeline editor. + +To cover such use cases, we provide _StreamPipes Functions_. Functions +are a great way to define custom processing logic based on previously +connected data streams. + +Functions can be registered in a similar way to pipeline elements, but define expected input +streams at startup time. Functions are started once the corresponding _extensions service_ starts +and run until the service is stopped. + +## Writing a function + +:::caution Work in Progress + +Functions are currently in preview mode and are not yet recommended for production usage. +APIs are subject to change in a future version. + +::: + + +To define a function, create a new extensions service using the [Maven Archetypes](06_extend-archetypes.md) or use an already existing service. + +### Skeleton + +Functions can be defined by creating a new class which extends the ``StreamPipesFunction`` class. + +The basic skeleton looks like this: + +```java +public class StreamPipesFunctionExample extends StreamPipesFunction { + + @Override + public FunctionId getFunctionId() { + return FunctionId.from("my-function-id", 1); + } + + @Override + public List requiredStreamIds() { + return List.of(""); + } + + @Override + public void onServiceStarted(FunctionContext context) { + // called when the service is started + } + + @Override + public void onEvent(Event event, String streamId) { + // called when an event arrives + } + + @Override + public void onServiceStopped() { + // called when the service is stopped + } +} + +``` + +The structure of a function class is easy to understand: +* _getFunctionId_ requires an identifier in form of a ``FunctionId``, which defines the id itself along with a version number that can be freely chosen. +* _requiredStreamIds_ expects a list of references to data streams that are already available in StreamPipes. See below to learn how to find the id of a stream in StreamPipes. +* _onServiceStarted_ is called once the extensions service is started and can be used to initialize the function. +* _onEvent_ is called every time a new event arrives and provides a ``streamId`` as a reference to the corresponding stream, which is useful in case multiple data streams are received by the function. +* _onServiceStopped_ is called when the extensions service is stopped and can be used to perform any required cleanup. + +### Getting a stream ID + +Functions require a reference to all data streams that should be retrieved by the function. +Currently, the only way to get the ID of a function is by navigating to the ``Asset Management`` view in the StreamPipes UI. +Create a new asset, click on ``Edit Asset`` and open ``Add Link`` in the _Linked Resources_ panel. +Choose ``Data Source`` as link type, select one of the available sources, copy the ``Resource ID`` and provide this ID in the ``requiredStreamIds`` method. + +### Function Context + +The ``onServiceStarted`` method provides a function context which provides several convenience methods to work with functions: + +* _getFunctionId_ returns the current function identifier +* _getConfig_ returns a reference to configuration options of the extensions service +* _getClient_ returns a reference to the StreamPipes client to interact with features from the REST API. +* _getStreams_ returns the data model of all data streams defined in the ``requiredStreamIds`` method. +* _getSchema_ returns the schema of a specific data stream by providing the ``streamId`` + + +## Registering a function + +Registering a function is easy and can be done in the _Init_ class of the service. +E.g., considering a service definition as illustrated below, simply call ``registerFunction`` and +provide an instance of your function. + +```java + + @Override + public SpServiceDefinition provideServiceDefinition() { + return SpServiceDefinitionBuilder.create("my-service-id", + "StreamPipes Function Example", + "", + 8090) + .registerFunction(new MyExampleFunction()) + .registerMessagingFormats( + new JsonDataFormatFactory()) + .registerMessagingProtocols( + new SpNatsProtocolFactory()) + .build(); + } + +``` + +## Metrics & Monitoring + +Similar to pipeline elements, function register at the StreamPipes core. +Running functions can be seen in the pipeline view of the user interface under _Functions_, right below the list of available pipelines. +Similar to pipelines, simple metrics, monitoring info and exceptions can be viewed in the _Details_ section of each function. diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migration-sd.md b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migration-sd.md new file mode 100644 index 000000000..1fd5200ad --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migration-sd.md @@ -0,0 +1,117 @@ +--- +id: extend-sdk-migration-service-discovery +title: "Migration Guide: New Service Discovery in 0.69.0" +sidebar_label: "Migration Guide: 0.69.0" +--- + + +## Introduction +As part of our roadmap towards a release 1.0, Apache StreamPipes 0.69.0 features a new service discovery approach along with performance improvements related to a new storage layer for pipeline element descriptions. + +The new service discovery approach is better suited for cloud-native scenarios, as the hostname of a pipeline element is now decoupled from its description. As such, StreamPipes now supports recovery of pipeline elements independent from their assigned host. +In addition, the new approach simplifies development of StreamPipes, e.g., in cases where the core is running in Docker and pipeline elements are developed on a local machine. In this case, the IP of the host machine should now be auto-discovered so that provision of environement variables should now be obsolete in most cases. +The second large improvement is related to the replacement of RDF4J as the storage engine with a NoSQL database. This leads to much faster load times (you will notice this especially at system startup). + +While we are heavily working towards a stable release 1.0, we decided to put our efforts into the remaining features required for 1.0 and do not provide an auto-migration related to some breaking changes. +Therefore, we recommend to reinstall StreamPipes when updating to 0.69.0. We currently plan to have at most two more releases before releasing the first 1.x version of StreamPipes. + +## Installation +* Before upgrading to 0.69.0, clean any existing installation (e.g., by running ``docker-compose down -v``) and make sure that no volumes of StreamPipes are left. +* Upgrade to the latest installer version (can be found at [streampipes/installer](https://github.com/apache/streampipes/tree/dev/installer)) +* Upon restart, make sure that the setup dialog appears (make sure that the new StreamPipes logo appears) and re-initialize the system. + +## SDK changes + +0.69.0 comes with a new ``ServiceDefinitionBuilder`` for pipelines, which simplifies the definition of a pipeline element. + +The ServiceDefinitionBuilder requires an ID of your extensions service, an optional title and description and a default port. +It is best to provide 8090 as the default port, so that this will be the standard port of all StreamPipes extensions services at deployment time in a containerized environment. +The port port can always be overriden by providing an ``SP_PORT`` environment variable. + +### Init class + +Modify the Init class of your pipeline element service as follows: + +```java +public class ExamplesInit extends StandaloneModelSubmitter { + + public static void main(String[] args) { + new ExamplesInit().init(); + } + + @Override + public SpServiceDefinition provideServiceDefinition() { + return SpServiceDefinitionBuilder.create("org.apache.streampipes.processors.examples.jvm", + "StreamPipes Code Examples", + "", + 8090) + .registerMessagingProtocols(new SpKafkaProtocolFactory(), new SpJmsProtocolFactory()) + .registerMessagingFormats(new JsonDataFormatFactory()) + .registerPipelineElement(new MyPipelineElementController()) + .registerAdapter(new MyAdapter()) + .build(); + } +} +``` + +You can now easily define a StreamPipes extensions service that supports both custom adapters and pipeline elements by using the following Maven dependency: +This is optional and no changes to your existing Maven dependencies (except the version, e.g., 0.69.0-SNAPSHOT) are required. + +```maven + + org.apache.streampipes + streampipes-container-extensions + +``` + + +### Configs +Prior to version 0.69.0, additionally configs had to be provided in a separate ``Config`` class. This is now obsolete - configs can be directly provided within the builder class as follows: + +```java + + @Override + public SpServiceDefinition provideServiceDefinition() { + return SpServiceDefinitionBuilder.create("org.apache.streampipes.processors.examples.jvm", + "StreamPipes Code Examples", + "", + 8090) + .registerPipelineElement(new MyPipelineElement()) + .registerAdapter(new MyAdapter()) + .addConfig("key", 1) + .addConfig("my-string-config", "myvalue") + .build(); + } +``` + +Configs can be easily accessed from the ``EventProcessorRuntimeContext`` (or ``EventSinkRuntimeContext``): + +```java +@Override + public void onInvocation(Parameters params, + SpOutputCollector spOutputCollector, + EventProcessorRuntimeContext context) { + + Integer myConfigValue = context.getConfigStore().getConfig().getInteger("key"); + } +``` + + +### Service Discovery +An extensions service can be started by executing the Init class. +StreamPipes will now automatically select the proper service IP address and register the service at the backend. +You can inspect the selected IP address in the console: + +``` +2024-05-16T11:03:37.158+02:00 INFO --- [ main] o.a.s.commons.networking.Networking : Using auto-discovered IP: 192.168.178.22 +2024-05-16T11:03:37.158+02:00 INFO --- [ main] o.a.s.commons.networking.Networking : Using port from provided environment variable SP_PORT: 7023 +2024-05-16T11:03:37.372+02:00 INFO --- [ main] a.s.s.e.StreamPipesExtensionsServiceBase : Registering service org.apache.streampipes.extensions.all.jvm with id org.apache.streampipes.extensions.all.jvm-FUt84Y at core +2024-05-16T11:03:37.814+02:00 INFO --- [ main] o.a.s.s.extensions.CoreRequestSubmitter : Successfully registered service at core. +2024-05-16T11:03:37.814+02:00 INFO --- [ main] a.s.s.e.StreamPipesExtensionsServiceBase : Registering 1 service configs for service org.apache.streampipes.extensions.all.jvm +``` + +In some (rare) cases, a non-resolvable IP will be selected. In this case, you can manually override the IP by providing a ``SP_HOST`` environment variable. This falls back to a similar behaviour as in pre-0.69.0-versions and will use the manually provided IP. + + + + diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migrations.md b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migrations.md new file mode 100644 index 000000000..bf822c1b8 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-migrations.md @@ -0,0 +1,179 @@ +--- +id: extend-sdk-migration +title: "SDK Guide: Pipeline Element Migration" +sidebar_label: "SDK: PE Migration" +--- + +Pipeline element migrations allow you to automatically update and migrate both existing and pipeline elements when a new +version of StreamPipes is installed. This means that whenever you upgrade StreamPipes, all existing and future +pipeline elements will be directly compatible with the new version without any manual interaction. Pipeline elements +include adapters, data processors, and data sinks. + +:::info +Migrations will make their debut in StreamPipes version `0.93.0` and will be an integral part of the system going +forward. +However, it's important to note that this feature is not available in any of the previous versions of StreamPipes. To +take full advantage of migrations and their benefits, it is recommended to upgrade to version `0.93.0` or later. This +will +ensure that you have access to the latest enhancements and maintain compatibility with the evolving StreamPipes +platform. +::: + +## Define Migrations + +Whenever a pipeline element, be it an adapter, data processor, or data sink, undergoes changes that result in +modifications to its configuration options, developers must additionally create a migration procedure. This migration +process should be capable of smoothly transitioning all affected instances from the previous version to the new one. +The migration itself is automatically managed and executed by StreamPipes. Developers are only responsible for two key +aspects: + +* **Implementing the concrete migration**: Developers need to craft the specific migration logic that facilitates the + seamless transition of configuration options. +* **Registering the migration**: Developers should register their migration procedures at the extensions service, + allowing StreamPipes to identify and apply the necessary updates to affected instances. + +By adhering to these two essential tasks, developers can ensure a hassle-free evolution of pipeline elements while +StreamPipes handles the orchestration of the migration process. + +The following gives a concrete example of creating a migration for +the [S7 adapter](./pe/org.apache.streampipes.connect.iiot.adapters.plc4x.s7.md). +Thereby, we assume this adapter has received a new input element which determines whether the connection should be made +authenticated or not. +This is represented by a simple boolean that is visualized as a toggle button in the UI. + +### Implementing a Concrete Migration + +StreamPipes offers three distinct migration mechanisms tailored to specific types of pipeline +elements: `IAdapterMigrator`, `IDataProcessorMigrator`, and `IDataSinkMigrator`. +These migration mechanisms are presented as interfaces and require the implementation of two fundamental methods: + +* `config()`: This method defines the configuration for the migration, encompassing all essential metadata related to + the migration process. +* `migrate()`: Within this method, the actual migration logic is to be implemented. It serves as the operational core + for facilitating the migration for the respective pipeline element. + +In accordance with the example described above, we will implement the `Plc4xS7AdapterMigrationV1` in the following. + +:::note +Before we begin, it's important to familiarize ourselves with two key conventions that guide our approach to migrations: + +* To maintain clarity and organization, all migration classes associated with a specific pipeline element are located + within a dedicated sub-package named `migration`. This sub-package is nested within the package of the respective + pipeline element. +* Migration classes are named according to a specific schema: `MigrationV`. For + example, if you are working on a migration for the PLC4x S7 adapter targeting version 1, the migration class would be + named `Plc4xS7AdapterMigrationV1`. +::: + +Let's begin with providing the migration's configuration: + +```java +@Override +public ModelMigratorConfig config() { + return new ModelMigratorConfig( + "org.apache.streampipes.connect.iiot.adapters.plc4x.s7", + SpServiceTagPrefix.ADAPTER, + 0, + 1 + ); +} +``` + +The migration config consists of the following four parts: + +* `targetAppId`: this needs to equal the app id of the targeted element +* `modelType`: the type of the element to be migrated, this can be one + of: `SpServiceTagPrefix.ADAPTER`, `SpServiceTagPrefix.DATA_PROCESSOR`, `SpServiceTagPrefix.DATA_SINK`. +* `fromVersion`: the version of the element that the migration expects as input +* `toVersion`: the version the element has after the migration (needs to be at least `fromVersion + 1`) + +The second step is to implement the actual migration logic. +In our example, we need to extend the existing static properties by an additional boolean property. + +```java +@Override +public MigrationResult migrate(AdapterDescription element, IStaticPropertyExtractor extractor) throws RuntimeException { + + var config = element.getConfig(); + + var slideToggle = new SlideToggleStaticProperty(); + slideToggle.setDefaultValue(false); + slideToggle.setLabel("Authentication required?"); + config.add(slideToggle); + + element.setConfig(config); + return MigrationResult.success(element); +} +``` + +We've completed all the necessary steps for our migration. The final task remaining is to register the migration within +the service definition. + +### Registering the Migration + +Only when the migration is registered at the service definition, the migration is sent to the StreamPipes core service. +Therefore, we need to add the migration to the same service definition as the element to migrate. +In our example this is defined in `ConnectAdapterIiotInit`: + +```java jsx {22-24} showLineNumbers +@Override +public SpServiceDefinition provideServiceDefinition() { + return SpServiceDefinitionBuilder.create("connect-adapter-iiot", + "StreamPipes connect worker containing adapters relevant for the IIoT", + "", + 8001) + .registerAdapter(new MachineDataSimulatorAdapter()) + .registerAdapter(new FileReplayAdapter()) + .registerAdapter(new IfmAlMqttAdapter()) + .registerAdapter(new RosBridgeAdapter()) + .registerAdapter(new OpcUaAdapter()) + .registerAdapter(new Plc4xS7Adapter()) + .registerAdapter(new Plc4xModbusAdapter()) + .registerAdapter(new KafkaProtocol()) + .registerAdapter(new MqttProtocol()) + .registerAdapter(new NatsProtocol()) + .registerAdapter(new HttpStreamProtocol()) + .registerAdapter(new PulsarProtocol()) + .registerAdapter(new RocketMQProtocol()) + .registerAdapter(new HttpServerProtocol()) + .registerAdapter(new TubeMQProtocol()) + .registerMigrators( + new Plc4xS7AdapterMigrationV1() + ) + .build(); +``` + +
+ +## How Migrations are Handled Internally + +Migrations are handled by an interplay between the Extension Service, which provides the migrations, +and the StreamPipes Core Service, which manages the migrations, as shown in the figure below: +Interplay of extensions service and core to handle migrations + +When an extensions service is initiated and has successfully registered itself with the core, it proceeds to send a +request to the core. This request includes a comprehensive list of all available migrations that have been registered +for it. Since this collection of migrations may encompass multiple migrations that affect the same pipeline element, +the migrations are first de-duplicated and then sorted based on their version range before being transmitted. + +Upon receiving these migrations, the core's actions can be categorized into two distinct parts: + +* Update descriptions for new elements +* Update descriptions for existing elements + +### Update Descriptions for New Elements + +Each migration transmitted from the extensions service to the core triggers the core to update the description of the +corresponding element stored in CouchDB. This is achieved by requesting the current configuration from the extensions +service and subsequently overwriting the existing configuration in the storage. + +### Update Descriptions for Existing Elements + +For each migration sent from the extensions service to the core, the core conducts a thorough check to determine if any +existing elements are affected by this migration. If such elements are identified, the extensions service is tasked with +requesting and subsequently executing the migration on behalf of the core. + +In scenarios where multiple applicable migrations exist for a single pipeline element, they are sequentially applied. +Success in this process allows the core to seamlessly update the configuration. However, if any issues arise, the +corresponding pipeline element is halted. In the case of processors and sinks, the associated pipeline is even marked +with a `needs attention` label, which comes apparent in the UI. diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-output-strategies.md b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-output-strategies.md new file mode 100644 index 000000000..feb224856 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-output-strategies.md @@ -0,0 +1,348 @@ +--- +id: extend-sdk-output-strategies +title: "SDK Guide: Output Strategies" +sidebar_label: "SDK: Output Strategies" +--- + +## Introduction +In StreamPipes, output strategies determine the output of a data processor. +As the exact input schema of a processor is usually not yet known at development time (as processors can be connected with any stream that matches their requirements), output strategies are a concept to define how an input data stream is transformed to an output data stream. + +The following reference describes how output strategies can be defined using the SDK. + +:::tip Code on Github + +For all examples, the code can be found on [Github](https://www.github.com/apache/streampipes-examples/tree/dev/streampipes-pipeline-elements-examples-processors-jvm/src/main/java/org/apache/streampipes/pe/examples/jvm/outputstrategy/) + +::: + + +## Reference + +The methods described below to create static properties are available in the ``ProcessingElementBuilder`` class and are usually used in the ``declareModel`` method of the controller class. + +As follows, we will use the following example event to explain how output strategies define the output of a data processor: + +```json +{ + "timestamp" : 1234556, + "temperature" : 37.0, + "deviceId" : "1" + +} +``` + +### Keep Output + +A ``KeepOutputStrategy`` declares that the output event schema will be equal to the input event schema. +In other terms, the processor does not change the schema, but might change the values of event properties. + +A keep output strategy can be defined as follows: + +```java + +@Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.outputstrategy" + + ".keep", "Keep output example example", "") + .requiredStream(StreamRequirementsBuilder. + create() + .requiredProperty(EpRequirements.anyProperty()) + .build()) + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + + // declaring a keep output strategy + .outputStrategy(OutputStrategies.keep()) + + .build(); + } + +``` + +According to the example above, the expected output event schema of the example input event would be: + +```json +{ + "timestamp" : 1234556, + "temperature" : 37.0, + "deviceId" : "1" + +} +``` + +Data processors that perform filter operations (e.g., filtering temperature values that are above a given threshold) are a common example for using keep output strategies. + + +### Fixed Output + +A ``FixedOutputStrategy`` declares that the data processor itself provides the event schema. The output schema does not depend on the input event. + +Fixed output strategies need to provide the event schema they produce at development time: + +```java + + @Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.outputstrategy" + + ".fixed", "Fixed output example", "") + .requiredStream(StreamRequirementsBuilder. + create() + .requiredProperty(EpRequirements.anyProperty()) + .build()) + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + + // the fixed output strategy provides the schema + .outputStrategy(OutputStrategies.fixed(EpProperties.timestampProperty("timestamp"), + EpProperties.doubleEp(Labels.from("avg", "Average value", ""), "avg", SO.Number))) + + .build(); + } + +``` + +In this example, we declare that the output schema always consists of two fields (``timestamp`` and ``avg``). + +Therefore, an output event should look like: + +```json +{ + "timestamp" : 1234556, + "avg" : 36.0 +} +``` + + +### Append Output + +An ``AppendOutputStrategy`` appends additional fields to a schema of an incoming event stream. For instance, data processors that perform enrichment operations usually make use of append output strategies. + +Similar to the fixed output strategy, the additional fields must be provided at development time in the controller method as follows: + +```java + @Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.outputstrategy" + + ".append", "Append output example", "") + + // boilerplate code not relevant here, see above + + // declaring an append output + .outputStrategy(OutputStrategies.append(EpProperties.integerEp(Labels.from("avg", + "The average value", ""), "avg", SO.Number))) + + .build(); + } +``` + +In this case, the output event would have an additional field ``avg``: + +```json +{ + "timestamp" : 1234556, + "temperature" : 37.0, + "deviceId" : "1", + "avg" : 123.0 + +} +``` + +### Custom Output + +In some cases, pipeline developers using the StreamPipes UI should be able to manually select fields from an input event schema. For such use cases, a ``CustomOutputStrategy`` can be used: + +```java + +@Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.outputstrategy" + + ".custom", "Custom output example", "") + + // boilerplate code not relevant here, see above + + // declaring a custom output + .outputStrategy(OutputStrategies.custom()) + + .build(); + } + +``` + +If a data processor defines a custom output strategy, the customization dialog in the pipeline editor will show a dialog to let users select the fields to keep: + +Number Parameter + +Taking our example, and assuming that the user selects both the ``timestamp`` and the ``temperature`` the expected output event should look like this: + +```json +{ + "timestamp" : 1234556, + "temperature" : 37.0 +} +``` + +How do we know which fields were selected once the data processor is invoked? Use the proper method from the extractor in the ``onInvocation`` method: + +```java +@Override + public ConfiguredEventProcessor onInvocation(DataProcessorInvocation graph, ProcessingElementParameterExtractor extractor) { + + List outputSelectors = extractor.outputKeySelectors(); + + return new ConfiguredEventProcessor<>(new DummyParameters(graph), DummyEngine::new); + } +``` + +### Transform Output + +A ``TransformOutputStrategy`` declares that one or more fields of an incoming event stream are transformed. Transformations can be applied to the datatype of the property, the runtime name of the property, or any other scheam-related declaration such as measurement units. + +#### Static Transform Operations + +Static transform operations do not depend on any user input (at pipeline development time) in order to know how to transform a field of an incoming event schema. + +Let's say our data processor transforms strings (that are actually a number) to a number datatype. In this case, we can use a static transform output strategy: + +```java + + @Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.outputstrategy" + + ".transform", "Transform output example example", "") + .requiredStream(StreamRequirementsBuilder. + create() + .requiredPropertyWithUnaryMapping(EpRequirements.stringReq(), Labels.from + ("str", "The date property as a string", ""), PropertyScope.NONE) + .build()) + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + + // static transform operation + .outputStrategy(OutputStrategies.transform(TransformOperations + .staticDatatypeTransformation("str", Datatypes.Long))) + + .build(); + } + +``` + +Note the mapping property that we use to determine which field of the input event should be transformed. + +The expected output event would look like this: + +```json +{ + "timestamp" : 1234556, + "temperature" : 37.0, + "deviceId" : 1 +} +``` + +#### Dynamic Transform Operations + +Sometimes, user input depends on the exact transform output. Let's take a field renaming processor as an example, which lets the user rename a field from an input event schema to another field name. +For such use cases, we can use a ``DynamicTransformOperation``: + +```java + + @Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.outputstrategy" + + ".transform", "Transform output example example", "") + .requiredStream(StreamRequirementsBuilder. + create() + .requiredPropertyWithUnaryMapping(EpRequirements.stringReq(), Labels.from + ("str", "The date property as a string", ""), PropertyScope.NONE) + .build()) + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + + // the text input to enter the new runtime name + .requiredTextparameter(Labels.from("new-runtime-name", "New Runtime Name", "")) + + // static transform operation + .outputStrategy(OutputStrategies.transform(TransformOperations + .dynamicRuntimeNameTransformation("str", "new-runtime-name"))) + + .build(); + } + +``` + +For dynamic transform operations, an additional identifier that links to another static property can be assigned and later be fetched in the ``onInvocation`` method. + +Assuming we want to rename the field ``temperature`` to ``temp``, the resulting output event should look like this: + +```json +{ + "timestamp" : 1234556, + "temp" : 37.0, + "deviceId" : 1 +} +``` + +### Custom Transform Output + +Finally, in some cases the output schema cannot be described at pipeline development time. For these (usually rare) cases, a ``CustomTransformOutput`` strategy can be used. + +In this case, a callback function will be invoked in the controller class just after a user has filled in any static properties and clicks on ``Save`` in the pipeline editor. + +To define a custom transform output, we need to implement an interface in the controller class: + +```java +public class CustomTransformOutputController extends + StandaloneEventProcessingDeclarer implements + ResolvesContainerProvidedOutputStrategy { + + +@Override + public EventSchema resolveOutputStrategy(DataProcessorInvocation processingElement, ProcessingElementParameterExtractor parameterExtractor) throws SpRuntimeException { + + } +} +``` + +In addition, the output strategy must be declared in the ``declareModel`` method: + +```java + +@Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.outputstrategy" + + ".customtransform", "Custom transform output example example", "") + .requiredStream(StreamRequirementsBuilder. + create() + .requiredPropertyWithUnaryMapping(EpRequirements.stringReq(), Labels.from + ("str", "The date property as a string", ""), PropertyScope.NONE) + .build()) + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + + // declare a custom transform output + .outputStrategy(OutputStrategies.customTransformation()) + + .build(); + } + +``` + +Once a new pipeline using this data processor is created and the configuration is saved, the ``resolveOutputStrategy`` method will be called, so that an event schema can be provided based on the given configuration. An extractor instance (see the guide on static properties) is available to extract the selected static properties and the connected event stream. + +```java +@Override + public EventSchema resolveOutputStrategy(DataProcessorInvocation processingElement, ProcessingElementParameterExtractor parameterExtractor) throws SpRuntimeException { + return new EventSchema(Arrays + .asList(EpProperties + .stringEp(Labels.from("runtime", "I was added at runtime", ""), "runtime", SO.Text))); + } +``` + +In this example, the output event schema should look like this: + +```json +{ + "runtime" : "Hello world!" +} +``` + diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-static-properties.md b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-static-properties.md new file mode 100644 index 000000000..39c2d5d6c --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-static-properties.md @@ -0,0 +1,267 @@ +--- +id: extend-sdk-static-properties +title: "SDK Guide: Static Properties" +sidebar_label: "SDK: Static Properties" +--- + +## Introduction +Static properties represent user-faced parameters that are provided by pipeline developers. +Processing elements can specify required static properties, which will render different UI views in the pipeline editor. + +The following reference describes how static properties can be defined using the SDK. + +:::tip Code on Github + +For all examples, the code can be found on [Github](https://github.com/apache/streampipes-examples/tree/dev/streampipes-pipeline-elements-examples-processors-jvm/src/main/java/org/apache/streampipes/pe/examples/jvm/staticproperty). + +::: + +## Reference + +The methods described below to create static properties are available in the ``ProcessingElementBuilder`` and ``DataSinkBuilder`` classes and are usually used in the ``declareModel`` method of the controller class. + +### Mapping property + +In StreamPipes, processing elements usually operate on fields of an event stream. For instance, a filter processor operates on a specific field from an input stream (e.g., a field measuring the temperature). +Typically, pipeline developers should select the exact field where the operations is applied upon by themselves. +As this field is not yet known at pipeline element development time (as it is defined by the pipeline developer in the pipeline editor), mapping properties serve to map a stream requirement to a specific field from the actual input event stream. + +### Unary mapping property + +A unary mapping property maps a stream requirement to an actual field of an event stream. Therefore, the ``StreamRequirementsBuilder`` provides the opportunity to directly add a mapping property based along with a property requirement: + +```java +.requiredStream(StreamRequirementsBuilder. + create() + .requiredPropertyWithUnaryMapping(EpRequirements.numberReq(), + Labels.from("mp-key", "My Mapping", ""), + PropertyScope.NONE) + .build()) +``` + +This leads to a selection dialog in the pipeline element customization which provides the user with a selection of all event properties (fields) from the input stream that match the specified property requirement: + +Text + +At invocation time, the value can be extracted in the ``onInvocation`` method as follows: + +```java +// Extract the mapping property value +String mappingPropertySelector = extractor.mappingPropertyValue("mp-key"); +``` + +Note that this method returns a ``PropertySelector``, which can be used by the event model to extract the actual value of this field. + +### N-ary mapping property + +N-ary mapping properties work similar to unary mapping properties, but allow the mapping of one requirement to multiple event properties matching the requirement: + +```java +.requiredStream(StreamRequirementsBuilder. + create() + .requiredPropertyWithNaryMapping(EpRequirements.numberReq(), + Labels.from("mp-key", "My Mapping", ""), + PropertyScope.NONE) + .build()) +``` + +This renders the following selection, where users can select more than one matching event property: + +Text + +The following snippet returns a list containing the property selectors of all event properties that have been selected: + +```java +// Extract the mapping property value +List mappingPropertySelectors = extractor.mappingPropertyValues("mp-key"); +``` + +### Free-Text Parameters + +A free-text parameter requires the pipeline developer to enter a single value - which can be a string or another primitive data type. +The input of free-text parameters can be restricted to specific value ranges or can be linked to the value set of a connected input data stream. + +#### Text Parameters + +A text parameter lets the user enter a string value. The following code line in the controller class + +```java +.requiredTextParameter(Labels.from(SP_KEY, "Example Name", "Example Description")) +``` + +leads to the following input dialog in the pipeline editor: + +Text + +Users can enter any value that will be converted to a string datatype. To receive the entered value in the ``onInvocation`` method, use the following method from the ``ParameterExtractor`` + +```java +String textParameter = extractor.singleValueParameter(SP_KEY, String.class); +``` + +#### Number parameters + +A number parameter lets the user enter a number value, either a floating-point number or an integer: + +```java +// create an integer parameter +.requiredIntegerParameter(Labels.from(SP_KEY, "Integer Parameter", "Example Description")) + +// create a float parameter +.requiredFloatParameter(Labels.from("float-key", "Float Parameter", "Example Description")) + +``` + +leads to the following input dialog in the pipeline editor only accepting integer values: + +Number Parameter + +The pipeline editor performs type validation and ensures that only numbers can be added by the user. To receive the entered value in the ``onInvocation`` method, use the following method from the ``ParameterExtractor`` + +```java +// Extract the integer parameter value +Integer integerParameter = extractor.singleValueParameter(SP_KEY, Integer.class); + +// Extract the float parameter value +Float floatParameter = extractor.singleValueParameter("float-key", Float.class); + +``` + +#### Numbers with value specification + +You can also specify the value range of a number-based free text parameter: + +```java +// create an integer parameter with value range +.requiredIntegerParameter(Labels.from(SP_KEY, "Integer Parameter", "Example Description"), 0, 100, 1) + +``` + +which renders the following input field: + +Number Parameter + +Receive the entered value in the same way as a standard number parameter. + +#### Free-text parameters linked to an event property + + +### Single-Value Selections + +Single-value selections let the user select from a pre-defined list of options. +A single-value selection requires to select exactly one option. + +```java +.requiredSingleValueSelection(Labels.from("id", "Example Name", "Example Description"), + Options.from("Option A", "Option B", "Option C")) + +``` + +Single-value selections will be rendered as a set of radio buttons in the pipeline editor: + +Number Parameter + +To extract the selected value, use the following method from the parameter extractor: + +```java +// Extract the selected value +String selectedSingleValue = extractor.selectedSingleValue("id", String.class); +``` + +:::tip Declaring options + +Sometimes, you may want to use an internal name that differs from the display name of an option. +For that, you can use the method Options.from(Tuple2{'<'}String, String{'>'}) and the extractor method selectedSingleValueInternalName. + +:::tip + + +### Multi-Value Selections + +Multi-value selections let the user select from a pre-defined list of options, where multiple or no option might be selected. + +```java +.requiredMultiValueSelection(Labels.from("id", "Example Name", "Example Description"), + Options.from("Option A", "Option B", "Option C")) + +``` + +Multi-value selections will be rendered as a set of checkboxes in the pipeline editor: + +Number Parameter + +To extract the selected value, use the following method from the parameter extractor: + +```java +// Extract the selected value +List selectedMultiValue = extractor.selectedMultiValues("id", String.class); +``` + +### Domain Concepts + +(coming soon...) + +### Collections + +You can also define collections based on other static properties. + +```java +// create a collection parameter +.requiredParameterAsCollection(Labels.from("collection", "Example Name", "Example " + + "Description"), StaticProperties.stringFreeTextProperty(Labels + .from("text-property","Text",""))) +``` + +While the items of the collection can be provided in the same way as the underlying static property, the UI provides buttons to add and remove items to the collections. + +Number Parameter + +To extract the selected values from the collection, use the following method from the parameter extractor: + +```java +// Extract the text parameter value +List textParameters = extractor.singleValueParameterFromCollection("collection", String.class); +``` + +### Runtime-resolvable selections + +In some cases, the options of selection parameters are not static, but depend on other values or might change at runtime. In this case, you can use runtime-resolvable selections. + +First, let your controller class implement ``ResolvesContainerProvidedOptions``: + +```java +public class RuntimeResolvableSingleValue extends + StandaloneEventProcessingDeclarer implements ResolvesContainerProvidedOptions { ... } +``` + +Next, define the parameter in the ``declareModel`` method: + +```java +// create a single value selection parameter that is resolved at runtime + .requiredSingleValueSelectionFromContainer(Labels.from("id", "Example Name", "Example " + + "Description")) +``` + +Finally, implement the method ``resolveOptions``, which will be called at runtime once the processor is used: + +```java + @Override + public List resolveOptions(String requestId, EventProperty linkedEventProperty) { + return Arrays.asList(new RuntimeOptions("I was defined at runtime", "")); + } +``` + +The UI will render a single-value parameter based on the options provided at runtime: + +Number Parameter + +The parameter extraction does not differ from the extraction of static single-value parameters. + + +:::info Multi-value selections + +Although this example shows the usage of runtime-resolvable selections using single value selections, the same also works for multi-value selections! + +::: + + diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-stream-requirements.md b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-stream-requirements.md new file mode 100644 index 000000000..409c5164d --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-sdk-stream-requirements.md @@ -0,0 +1,181 @@ +--- +id: extend-sdk-stream-requirements +title: "SDK Guide: Stream Requirements" +sidebar_label: "SDK: Stream Requirements" +--- + +## Introduction + +Data processors and data sinks can define ``StreamRequirements``. Stream requirements allow pipeline elements to express requirements on an incoming event stream that are needed for the element to work properly. +Once users create pipelines in the StreamPipes Pipeline Editor, these requirements are verified against the connected event stream. +By using this feature, StreamPipes ensures that only pipeline elements can be connected that are syntactically and semantically valid. + +This guide covers the creation of stream requirements. Before reading this section, we recommend that you make yourself familiar with the SDK guide on [data processors](extend-first-processor). + + +:::tip Code on Github + +For all examples, the code can be found on [Github](https://www.github.com/apache/streampipes-examples/tree/dev/streampipes-pipeline-elements-examples-processors-jvm/src/main/java/org/apache/streampipes/pe/examples/jvm/requirements/). + +::: + + +## The StreamRequirementsBuilder + +Stream requirements can be defined in the ``declareModel`` method of the pipeline element class. Start with a method body like this: + +```java + +@Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create(ID, PIPELINE_ELEMENT_NAME, DESCRIPTION) + .requiredStream(StreamRequirementsBuilder. + create() + + .build()) + + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + .outputStrategy(OutputStrategies.keep()) + + .build(); + } +``` + +The ``StreamRequirementsBuilder`` class provides methods to add stream requirements to a pipeline element. + +## Requirements on primitive fields + +As a very first example, let's assume we would like to create a data processor that filters numerical values that are above a given threshold. +Consequently, any data stream that is connected to the filter processor needs to provide a numerical value. + +The stream requirement would be assigned as follows: + +```java +@Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create(ID, PIPELINE_ELEMENT_NAME, DESCRIPTION) + .requiredStream(StreamRequirementsBuilder + .create() + .requiredProperty(EpRequirements.numberReq()) + .build()) + + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + .outputStrategy(OutputStrategies.keep()) + + .build(); + } +``` + +Note the line starting with ``requiredProperty``, which requires any stream to provide a datatype of type ``number``. + +In many cases, you'll want to let the user select a specific field from a data stream from all available fields that match the specified requirement. For that, you simply use the method ``requiredPropertyWithUnaryMapping`` as follows: + +```java +@Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create(ID, PIPELINE_ELEMENT_NAME, DESCRIPTION) + .requiredStream(StreamRequirementsBuilder + .create() + .requiredPropertyWithUnaryMapping(EpRequirements.numberReq(), + Labels.from("number-mapping", "The value that should be filtered", ""), PropertyScope.NONE) + .build()) + + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + .outputStrategy(OutputStrategies.keep()) + + .build(); + } +``` + +See also the developer guide on [static properties](extend-sdk-static-properties) to better understand the usage of ``MappingProperties``. + +Requirements on primitive fields can be specified for all common datatypes: + +```java + @Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.requirements" + + ".simple", "Simple requirements specification examples", "") + .requiredStream(StreamRequirementsBuilder. + create() + .requiredProperty(EpRequirements.numberReq()) // any number + .requiredProperty(EpRequirements.doubleReq()) // any field of type double + .requiredProperty(EpRequirements.booleanReq()) // any field of type boolean + .requiredProperty(EpRequirements.integerReq()) // any field of type integer + .requiredProperty(EpRequirements.stringReq()) // any field of type string + + .requiredProperty(EpRequirements.anyProperty()) // any field allowed (no restriction) + .requiredProperty(EpRequirements.timestampReq()) // any timestamp field + .build()) + + + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + .outputStrategy(OutputStrategies.keep()) + + .build(); +``` + +### Specifying semantics + +For some algorithms, only specifying the datatype is not sufficient. Let's consider a geofencing algorithm that detects the precense some geospatial coordinate (e.g., from a vehicle) within a given location. + +You could specify something like this: + +```java + StreamRequirementsBuilder + .create() + .requiredPropertyWithUnaryMapping(EpRequirements.doubleEp(), Labels.from("mapping-latitude", "Latitude", ""), PropertyScope.NONE) + .requiredPropertyWithUnaryMapping(EpRequirements.doubleEp(), Labels.from("mapping-longitude", "Longitude", ""), PropertyScope.NONE) + .build() +``` + +However, this would allow users to create strange pipelines as users could connect any stream containing a double value to our geofencing algorithm. +To avoid such situations, you can also specify requirements based on the semantics of a field: + +```java + StreamRequirementsBuilder + .create() + .requiredPropertyWithUnaryMapping(EpRequirements.domainPropertyReq(SO.Latitude), Labels.from("mapping-latitude", "Latitude", ""), PropertyScope.NONE) + .requiredPropertyWithUnaryMapping(EpRequirements.domainPropertyReq(SO.Longitude), Labels.from("mapping-longitude", "Longitude", ""), PropertyScope.NONE) + .build() +``` + +Note that in this case, we make use of Schema.org's ``Latitude`` concept ([https://schema.org/latitude](https://schema.org/latitude)). StreamPipes already includes popular vocabularies for specifying semantics. You are also free to use your own vocabularies. + + +## Requirements on lists + +Similarly to primitive requirements, you can define processors that require data streams with list fields, see the following examples: + +```java +@Override + public DataProcessorDescription declareModel() { + return ProcessingElementBuilder.create("org.streampipes.examples.requirements" + + ".list", "List requirements specification examples", "") + .requiredStream(StreamRequirementsBuilder. + create() + .requiredProperty(EpRequirements.listRequirement(Datatypes.Integer)) + .requiredProperty(EpRequirements.listRequirement(Datatypes.Double)) + .requiredProperty(EpRequirements.listRequirement(Datatypes.Boolean)) + .requiredProperty(EpRequirements.listRequirement(Datatypes.String)) + .build()) + + + .supportedProtocols(SupportedProtocols.kafka()) + .supportedFormats(SupportedFormats.jsonFormat()) + .outputStrategy(OutputStrategies.keep()) + + .build(); + } +``` + +## Requirements on nested properties + +(coming soon, see the Javadoc for now) + + + diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-setup.md b/website-v2/versioned_docs/version-0.95.0/06_extend-setup.md new file mode 100644 index 000000000..12a96eb8e --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-setup.md @@ -0,0 +1,50 @@ +--- +id: extend-setup +title: Development Setup +sidebar_label: Development Setup +--- + +Pipeline elements in StreamPipes are provided as standalone microservices. New pipeline elements can be easily developed using the provided Maven archetypes and can be installed in StreamPipes at runtime. + +In this section, we describe our recommended minimum setup for locally setting up a development instance of StreamPipes needed to develop, run and test new pipeline elements. + +## IDE & required dev tools +StreamPipes does not have specific requirements on the IDE - so feel free to choose the IDE of your choice. +The only requirements in terms of development tools are that you have Java 17 and Maven installed. + +## StreamPipes CLI: Docker-based local StreamPipes instance +In order to quickly test developed pipeline elements without needing to install all services required by StreamPipes, we provide a CLI tool that allows you to selectively start StreamPipes components. +The CLI tool allows to switch to several templates (based on docker-compose) depending on the role. + +The documentation on the usage of the CLI tool is available [here](06_extend-cli.md). + +## Override the SP_HOST variable + +By default, the backend/core of StreamPipes registers itself within StreamPipes' service discovery mechanism using an auto-discovered hostname. +Usually, this will be an IP address from the Docker network, which is not resolvable from outside. Therefore, for local development you need to override the hostname with an IP address which is accessible from your local host where you develop extensions. +When using the CLI, open the CLI folder ``installer/cli``, navigate to ``deploy/standalone/backend``, open the ``docker-compose.dev.yml`` file and add the SP_HOST env variable, e.g. + +``` +version: "3.4" +services: + backend: + ports: + - "8030:8030" + environment: + - SP_HOST=host.docker.internal +``` + +Note that host.docker.internal will work as an alias under Docker for Desktop on Windows and Mac, but not on Linux or M1. In this case, provide a resolvable hostname or IP address manually. + +## Starter projects + +Now, once you've started the development instance, you are ready to develop your very first pipeline element. +Instead of starting from scratch, we recommend using our provided maven archetypes: + +### Maven archetypes + +Create the Maven archetype as described in the [Maven Archetypes](06_extend-archetypes.md) guide. + +### Examples + +We provide several examples that explain the usage of some concepts in this [Github repo](https://github.com/apache/streampipes-examples). diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-adapters.md b/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-adapters.md new file mode 100644 index 000000000..4e95cba9a --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-adapters.md @@ -0,0 +1,612 @@ +--- +id: extend-tutorial-adapters +title: "Tutorial: Build Custom Adapters" +sidebar_label: "Tutorial: Adapters" +--- + +In this tutorial, we will create a new data source consisting of a single data stream. +By the end of the tutorial, you will be able to implement custom adapters that allow you to connect to data sources +other than those officially supported by StreamPipes. +To do this, we will split the tutorial into two parts. +The [first part](#building-a-basic-adapter) focuses on creating the adapter and defining the event stream. +At the end, we will have a working adapter that produces an event stream that can be used in StreamPipes. +This adapter does not provide any way to configure its behavior, so in +the [second part](#building-a-more-advanced-adapter-by-processing-ui-input) of the tutorial +to show how we can extend our existing adapter to be configurable via the UI. + +:::info +This tutorial shows how to build your own type of adapter. +It is intended for people who are interested in extending StreamPipes to meet their own needs. +If you are here to explore StreamPipes and are interested in using an adapter, you may want to +continue [here](./03_use-connect.md). +::: + +## Objective + +We are going to create an adapter that will simulate a stream of data generated by a control station in a logistics +center that is used to sort packages. +This station consists of two sensors: a light barrier that detects when a package passes through, and a weight sensor. + +This sensor produces a continuous stream of events containing the current time stamp, an indicator of whether a package +is present or the conveyor is empty, and the weight of the package in kilograms. +The events are published in JSON format as follows + +```json +{ + "timestamp": 1697720916959, + "parcelPresent": true, + "weight": 3.520 +} +``` + +In the following section, we will show you how to develop an adapter that is capable of generating this stream so that +it is available for further processing in StreamPipes. + +## Project Set Up + +Instead of creating a new project from scratch, we recommend to use our Maven archetype to create a new project +skeleton (`streampipes-archetype-extensions-jvm`). +Enter the following command in a command line of your choice (please ensure +that [Apache Maven](https://maven.apache.org/install.html) isinstalled): + +```bash +mvn archetype:generate \ +-DarchetypeGroupId=org.apache.streampipes -DarchetypeArtifactId=streampipes-archetype-extensions-jvm \ +-DarchetypeVersion=0.93.0 -DgroupId=org.apache.streampipes \ +-DartifactId=streampipes-archetype-extensions-jvm -DclassNamePrefix=ParcelControlStation -DpackageName=parcelcontrol +``` + +This command will ask you for input twice, you can just skip both of them by hitting *enter*. +The first dialog sets the version to use for our `streampipes-archetype-extensions-jvm` module. +Feel free to change this if you like. + +```bash +Define value for property 'version' 1.0-SNAPSHOT: : + + Y: : +``` + +The `mvn archetype:generate` command generates some required files, the required file structure, and some boilerplate +code. +The generated file structure should look like the following: + +:::info +Note that you can customize the parameters of the mvn command to affect the file structure and file naming. +::: + +```bash + +|streampipes-archetype-extensions # name is determined by '-DartifactId' +|-- development +| |-- env +|-- src +| |-- main +| | |-- java.org.apache.streampipes # name after .java. is determined by '-DgroupId' +| | | |-- pe.parcelcontrol # name after .pe. is determined by '-DpackageName' +| | | | |-- ParcelControlStationDataProcessor.java # class name is determined by '-DclassNamePrefix' +| | | | |-- ParcelControlStationDataSink.java +| | | | |-- ParcelControlStationGenericAdapter.java +| | | | |-- ParcelControlStationSpecificAdapter.java +| | | |-- Init.java +| | |-- resources +| | | |-- org.apache.streampipes.pe.parcelcontrol.genericadapter +| | | | |-- documentation.md +| | | | |-- icon.png +| | | | |-- strings.en +| | | |-- org.apache.streampipes.pe.parcelcontrol.processor +| | | | |-- documentation.md +| | | | |-- icon.png +| | | | |-- strings.en +| | | |-- org.apache.streampipes.pe.parcelcontrol.sink +| | | | |-- documentation.md +| | | | |-- icon.png +| | | | |-- strings.en +| | | |-- org.apache.streampipes.pe.parcelcontrol.specificadapter +| | | | |-- documentation.md +| | | | |-- icon.png +| | | | |-- strings.en +| |-- test.java.org.apache.streampipes # name after .java. is determined by '-DgroupId' +| | |-- InitTest.java +|-- Dockerfile +|-- pom.xml + +``` + +:::tip +In addition to the basic project skeleton, the sample project also includes a sample `Dockerfile` that you can use to +package your application into a Docker container. +::: + +## Building a Basic Adapter + +In the following, we will demonstrate how to use the boilerplate code generated by the Maven plugin ( +see [Project setup](#project-setup)). +Within this section, we will focus on creating an event stream that can be used within StreamPipes. +The following section shows how to configure the created adapter with UI input. + +Attentive readers may have noticed that two adapter classes have been generated. +We will focus on the `ParcelControlStationSpecificAdapter` first, the `ParcelControlStationSimulatorGenericAdapter` will +be used later for more advanced adapter features. +First, let us take a look at the `ParcelControlStationSpecificAdapter.java` file as generated by the Maven +archetype. + +```java jsx showLineNumbers +package org.apache.streampipes.pe.parcelcontrol; + +import org.apache.streampipes.commons.exceptions.connect.AdapterException; +import org.apache.streampipes.extensions.api.connect.IAdapterConfiguration; +import org.apache.streampipes.extensions.api.connect.IEventCollector; +import org.apache.streampipes.extensions.api.connect.StreamPipesAdapter; +import org.apache.streampipes.extensions.api.connect.context.IAdapterGuessSchemaContext; +import org.apache.streampipes.extensions.api.connect.context.IAdapterRuntimeContext; +import org.apache.streampipes.extensions.api.extractor.IAdapterParameterExtractor; +import org.apache.streampipes.model.AdapterType; +import org.apache.streampipes.model.connect.guess.GuessSchema; +import org.apache.streampipes.sdk.builder.adapter.AdapterConfigurationBuilder; +import org.apache.streampipes.sdk.builder.adapter.GuessSchemaBuilder; +import org.apache.streampipes.sdk.helpers.Labels; +import org.apache.streampipes.sdk.helpers.Locales; + +import java.util.HashMap; +import java.util.Map; + +public class ParcelControlStationSpecificAdapter implements StreamPipesAdapter { + + private boolean running = false; + + @Override + public IAdapterConfiguration declareConfig() { + return AdapterConfigurationBuilder.create( + "org.apache.streampipes.pe.parcelcontrol.specificadapter", + ParcelControlStationSpecificAdapter::new + ) + .withAssets(Assets.DOCUMENTATION, Assets.ICON) + .withCategory(AdapterType.Manufacturing) + .withLocales(Locales.EN) + .buildConfiguration(); + } + + @Override + public void onAdapterStarted(IAdapterParameterExtractor extractor, + IEventCollector collector, + IAdapterRuntimeContext adapterRuntimeContext) throws AdapterException { + + Runnable demo = () -> { + while (running) { + // make event + Map event = new HashMap<>(); + // forward the event to the adapter pipeline + collector.collect(event); + } + }; + running = true; + new Thread(demo).start(); + } + + @Override + public void onAdapterStopped(IAdapterParameterExtractor extractor, + IAdapterRuntimeContext adapterRuntimeContext) throws AdapterException { + + // do cleanup + running = false; + } + + @Override + public GuessSchema onSchemaRequested(IAdapterParameterExtractor extractor, + IAdapterGuessSchemaContext adapterGuessSchemaContext) throws AdapterException { + + // build the schema by adding properties to the schema builder and a preview if possible + return GuessSchemaBuilder + .create() + .build(); + } + } + +``` + +The class extends `StreamPipesAdapter`, which is the interface that all adapters within StreamPipes must implement. +This interface requires us to implement four methods: + +* `declareConfig()`: This method is expected to return the configuration of the adapter. The configuration includes + metadata about the adapter and its input parameters. +* `onAdapterStarted()`: This method is expected to contain the actual adapter logic. It is called when the adapter is + started, and is responsible for sending incoming data to StreamPipes as an event. +* `onAdapterStopped()`: This method is called when the adapter is stopped and is responsible for gracefully exiting the + adapter. + gracefully and usually performs some cleanup tasks. +* `onSchemaRequested()`: This method is expected to return the schema of the event stream. This is ideally done + dynamically based on some incoming data (*guess*) or provided statically if not otherwise possible. + +### Describing the Adapter via the Configuration + +The standard code generated here is already sufficient for us. +So let's have a quick look at the important aspects: + +* `Line 4`: Here we define a unique identifier for our adapter. This allows us to identify all instances of the same + adapter. Including your own namespace is always a good choice to avoid conflicts. +* `Line 7`: Here we define what assets are available for this adapter. In this case, we provide a documentation file and + an icon. Both assets are located in the `resource' directory (see file tree above). +* `Line 8`: This defines a rough categorization along predefined adapter types. +* `Line 9`: Here we define which locales are available for this adapter. Since we only provide one `strings.en' file so + far (see file tree above), the current selection is sufficient. Theoretically you can support multiple languages, but + this is not fully supported yet. + +```java jsx {4,7-9} showLineNumbers + @Override + public IAdapterConfiguration declareConfig() { + return AdapterConfigurationBuilder.create( + "org.apache.streampipes.pe.parcelcontrol.specificadapter", + ParcelControlStationSpecificAdapter::new + ) + .withAssets(Assets.DOCUMENTATION, Assets.ICON) + .withCategory(AdapterType.Manufacturing) + .withLocales(Locales.EN) + .buildConfiguration(); + } +``` + +Before we continue, let's quickly have a look at the `strings.en` file that defines our locales. +Here we can define a meaningful and human-readable adapter tile in the first line and a short description: + +```text +org.apache.streampipes.pe.parcelcontrol.specificadapter.title=Parcel Control Station (simple) +org.apache.streampipes.pe.parcelcontrol.specificadapter.description=This adapter simulates data coming from a parcel control station in a logistics center. +``` + +Now we have successfully configured our adapter and prepared all descriptive elements, we can focus on the actual logic. + +### Creating the Data Stream + +The logic that creates events that are then populated via StreamPipes is defined in `onAdapterStarted()`. +Within this method, connectors usually connect to the data source and extract data. +In our case, however, we simply want to create some sample data directly. +The two main parts that should always happen within this method are highlighted in the provided skeleton code: + +* `Line 10`: Creating an event is crucial for our adapters. This event is then filled with data by the adapter before it + is distributed. +* `Line 13`: The event must finally be passed to the `collector`, which then takes the data and distributes it within + StreamPipes in the form of a [data stream](./02_concepts-overview.md#data-stream). + +```java jsx {10,13} showLineNumbers +@Override +public void onAdapterStarted(IAdapterParameterExtractor extractor, + IEventCollector collector, + IAdapterRuntimeContext adapterRuntimeContext) throws AdapterException { + + Runnable demo = () -> { + while (running) { + + // make event + Map event = new HashMap<>(); + + // forward the event to the adapter pipeline + collector.collect(event); + } + }; + running = true; + new Thread(demo).start(); +} +``` + +So the only thing left to do is to create the actual events. +In our scenario, we want to create two types of events: one describing an empty conveyor and one describing a detected +and weighed package. +To keep the implementation simple, we simply want to have a parcel event every five seconds. We can implement this as +follows: + +```java + Runnable parcelControl = () -> { + while (running) { + + // get the current time in seconds + long timestamp = System.currentTimeMillis(); + long timeInSeconds = (int) timestamp / 1000; + + // make event + Map event = new HashMap<>(); + event.put("timestamp", timestamp); + + if (timeInSeconds % 5 == 0) { + event.put("parcelPresent", true); + event.put("weight", ThreadLocalRandom.current().nextDouble(0, 10)); + + } else { + event.put("parcelPresent", false); + event.put("weight", 0); + } + + // forward the event to the adapter pipeline + collector.collect(event); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + }; + running = true; + new Thread(parcelControl).start(); +``` + +This is already enough to get a data stream into StreamPipes. +As the next step we need to describe to event schema. + +### Defining the Event Schema + +In StreamPipes, each data stream comes with an event schema that describes what information the event contains, +in what data formats, and some semantic type information. +This allows StreamPipes to provide easy and convenient stream handling with a lot of automatic conversions and +validations. +For example, whether a particular data processor is suitable for a given event stream. +This event schema is provided by `onSchemaRequested()`: + +```java +@Override +public GuessSchema onSchemaRequested(IAdapterParameterExtractor extractor, + IAdapterGuessSchemaContext adapterGuessSchemaContext) throws AdapterException { + + // build the schema by adding properties to the schema builder and a preview if possible + return GuessSchemaBuilder + .create() + .build(); + } + +``` + +Normally, the event schema is determined automatically and dynamically, since an adapter is usually quite generic (read +more in the [Advanced section](#advanced)). +But in our case, we already know the event schema, and it never changes, so we can just define it: + +```java jsx {3,13-20} showLineNumbers +@Override +public GuessSchema onSchemaRequested(IAdapterParameterExtractor extractor, + IAdapterGuessSchemaContext adapterGuessSchemaContext) throws AdapterException { + + // build the schema by adding properties to the schema builder and a preview if possible + return GuessSchemaBuilder.create() + .property(timestampProperty("timestamp")) + .sample("timestamp", System.currentTimeMillis()) + .property(PrimitivePropertyBuilder + .create(Datatypes.Boolean, "parcelPresent") + .label("Parcel Present") + .description("Indicates if a parcel is weighed.") + .domainProperty(SO.BOOLEAN) + .scope(PropertyScope.MEASUREMENT_PROPERTY) + .build()) + .sample("parcelPresent", true) + .property(PrimitivePropertyBuilder + .create(Datatypes.Double, "weight") + .label("Parcel Weight") + .description("Parcel weight") + .domainProperty(SO.WEIGHT) + .scope(PropertyScope.MEASUREMENT_PROPERTY) + .build()) + .sample("weight", 3.520) + .build(); +``` + +An attribute of an Event is referred to as `property` in StreamPipes. +So in our case we have three properties. +Since StreamPipes creates a sample event in the UI when configuring the adapter ( +see [here](./03_use-connect.md#schema-editor)), +providing a meaningful sample value for every property allows StreamPipes to demonstrate its full potential. + +Since every event schema is required to have a timestamp property, we provide a convenience definition (see `line 3`). +For all other properties the recommend way of definition is using the `PrimitivePropertyBuilder` (see `line 13-20`) and +consists of the following steps: + +* `Line 14`: every property must have a data type specified and a property name +* `Line 15`: In addition to the property name we can define a label that is designed for the end user and shown in the + UI. +* `Line 16`: Assigns a human-readable description to the event property. The description is used in the StreamPipes UI + for better explaining users the meaning of the property. +* `Line 17`: Specifies the semantics of the property (e.g., whether a double value stands for weight or temperature + value). +* `Line 18`: Assigns a property scope to the event property. This determines how the property is handled internally. + +:::note +StreamPipes does not require you to provide all of this information about a property. +Anything beyond line `14` (up to line `20`) is optional, but the more you provide, the better StreamPipes can show it's +full potential and feature richness. +::: + +This makes our adapter almost complete, there is only one little step left. + +### Defining the Adapter Termination + +As a final step, we need to define what should happen if the adapter is stopped. +In general, the adapter should not fire any events after that. +Normally, this step includes things like closing connections and clearing resources. +In our case this is quite simple, we just need to stop our thread: + +```java +@Override +public void onAdapterStopped(IAdapterParameterExtractor extractor, + IAdapterRuntimeContext adapterRuntimeContext) throws AdapterException { + + // do cleanup + running = false; +} +``` + +Now it's time to start our adapter and observe it in action! + +### Register and Run the Adapter + +Before we actually use our adapter, let's take a quick look at the `Init` class. This class is responsible for +registering our adapter service with the core to make the adapter available in StreamPipes. +This is done within `provideServiceDefinition()`. Since we don't have the generic adapter ready yet, +we'll comment out its registration (`line 7`). Now we can run the `Init` class to register the adapter with your running +StreamPipes instance. If you don't have a running instance at your hand, +you can take a look at our [Installation Guide](./01_try-installation.md). + +```java jsx {7-8} showLineNumbers +@Override +public SpServiceDefinition provideServiceDefinition() { + return SpServiceDefinitionBuilder.create("org.apache.streampipes", + "human-readable service name", + "human-readable service description", 8090) + .registerRuntimeProvider(new StandaloneStreamPipesRuntimeProvider()) + //.registerAdapter(new ParcelControlStationGenericAdapter()) + .registerAdapter(new ParcelControlStationSpecificAdapter()) + .registerMessagingFormats( + new JsonDataFormatFactory(), + new CborDataFormatFactory(), + new SmileDataFormatFactory(), + new FstDataFormatFactory()) + .registerMessagingProtocols( + new SpKafkaProtocolFactory(), + new SpJmsProtocolFactory(), + new SpMqttProtocolFactory(), + new SpNatsProtocolFactory(), + new SpPulsarProtocolFactory()) + .build(); +} + ``` + +:::tip +When executing the `main()` method of the `Init` class, make sure that all environment variables are set from +the `development/env` file are set. +If they are not set, the adapter may not be able to register with StreamPipes. +::: + +Once you see the following log message in the console, the adapter is ready, and you can switch to the UI of your +StreamPipes instance. + +```bash +s.s.e.c.ConnectWorkerRegistrationService : Successfully connected to master. Worker is now running. +``` + +Please go to the connect module and click on `New Adapter`, +you should now be able to see your adapter `Parcel Control Station (simple)`: +Demo of parcel adapter + +The adapter runs successfully in StreamPipes, you can now play around with the data stream that the +adapter, or continue with the next section to learn how to make an adapter configurable through the UI. + +### Building a more Advanced Adapter by Processing UI Input + +In this section, we will extend our previous build apter by adding the ability to configure the minimum and maximum +package +in the UI from which the weight value is retrieved. +The beauty of building adapters for StreamPipes is that you don't have to worry about the UI. +StreamPipes provides a set of pre-built input elements for adapters that you can simply add to your adapter +configuration. +So the first thing we need to customize is `declareConfig()`: + +```java jsx {10-11} showLineNumbers +@Override +public IAdapterConfiguration declareConfig() { + return AdapterConfigurationBuilder.create( + "org.apache.streampipes.pe.parcelcontrol.specificadapter", + ParcelControlStationSpecificAdapter::new + ) + .withAssets(Assets.DOCUMENTATION, Assets.ICON) + .withCategory(AdapterType.Manufacturing) + .withLocales(Locales.EN) + .requiredFloatParameter(Labels.withId("min-weight"), 0.0f) + .requiredFloatParameter(Labels.withId("max-weight"), 10.f) + .buildConfiguration(); +} + +``` + +In line `9-10` we have introduced two input parameters that expect float values as input. They have a default value +of `0` or `10` resp. The defined identifier (`min-weight` and `max-weight`) can be used two provide a caption and +a description via the `strings.en` file: + +```text +min-weight.title=Minimum Parcel Weight +min-weight.description=The lower bound from which the weight values are sampled randomly. + +max-weight.title=Maximum Parcel Weight +max-weight.description=The upper bound from which the weight values are sampled randomly. +``` + +As a last step, we now need to modify the calculation of the parcel weight, so that the provided parameters are actually +applied. +This is done in `onAdapterStarted()`. + +```java jsx {6-9,24} showLineNumbers +@Override +public void onAdapterStarted(IAdapterParameterExtractor extractor, + IEventCollector collector, + IAdapterRuntimeContext adapterRuntimeContext) throws AdapterException { + + var ex = extractor.getStaticPropertyExtractor(); + + float minWeight = ex.singleValueParameter("min-weight", Float.class); + float maxWeight = ex.singleValueParameter("max-weight", Float.class); + + Runnable parcelControl = () -> { + while (running) { + + // get the current time in seconds + long timestamp = System.currentTimeMillis(); + long timeInSeconds = (int) timestamp / 1000; + + // make event + Map event = new HashMap<>(); + event.put("timestamp", timestamp); + + if (timeInSeconds % 5 == 0) { + event.put("parcelPresent", true); + event.put("weight", ThreadLocalRandom.current().nextDouble(minWeight, maxWeight)); + + } else { + event.put("parcelPresent", false); + event.put("weight", 0); + } + + // forward the event to the adapter pipeline + collector.collect(event); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + }; + running = true; + new Thread(parcelControl).start(); +} +``` + +* line `6-9`: We use an `StaticPropertyExtractor` to retrieve both user inputs +* line `24`: We calculate the parcel weight by passing the configured values vor the minimum and maximum value. + +You can now run the `main()` method of the `Init` class to register the adapter at StreamPipes. +The UI dialog to create a new instance of our parcel control station adapter looks now the following: +Adapter with UI dialog + +:::caution +Please make sure that you uninstall the parcel adapter in `Install Pipeline Elements` before +you restart the execution of the `Init` class, if you have already done so. +Otherwise, the changes made in this section will have no effect. +::: + +### Read More + +Congratulations! You've just created your first StreamPipes adapter 🎉
+ +There are many more things to explore and data sources can be defined in much more detail. +If this is of interest to you, the [advanced section](#advanced) will satisfy your needs. + +If anything within this tutorial did not work for you or you had problems following it, +please feel free to provide some feedback by opening an [issue on GitHub](https://github.com/apache/streampipes/issues/new?assignees=&labels=bug%2Cdocumentation%2Cwebsite&projects=&template=doc_website_issue_report.yml). + + diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-processors.md b/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-processors.md new file mode 100644 index 000000000..4c2d5ee25 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-processors.md @@ -0,0 +1,454 @@ +--- +id: extend-tutorial-data-processors +title: "Tutorial: Data Processors" +sidebar_label: "Tutorial: Data Processors" +--- + +In this tutorial, we will add a new data processor. + +From an architectural point of view, we will create a self-contained service that includes the description of the data +processor and an implementation. + +## Objective + +We are going to create a new data processor that realizes a simple geofencing algorithm - we detect vehicles that enter +a specified radius around a user-defined location. +This pipeline element will be a generic element that works with any event stream that provides geospatial coordinates in +form of a latitude/longitude pair. + +The algorithm outputs every location event once the position has entered the geofence. + +:::note + +The implementation in this tutorial is pretty simple - our processor will fire an event every time the GPS location is +inside the geofence. +In a real-world application, you would probably want to define a pattern that recognizes the _first_ event a vehicle +enters the geofence. + +This can be easily done using a CEP library. + +::: + +## Project setup + +Instead of creating a new project from scratch, we recommend to use the Maven archetype to create a new project +skeleton (streampipes-archetype-extensions-jvm). +Enter the following command in a command line of your choice (Apache Maven needs to be installed): + +``` +mvn archetype:generate \ +-DarchetypeGroupId=org.apache.streampipes -DarchetypeArtifactId=streampipes-archetype-extensions-jvm \ +-DarchetypeVersion=0.93.0 -DgroupId=my.groupId \ +-DartifactId=my-example -DclassNamePrefix=MyExample -DpackageName=mypackagename +``` + +You will see a project structure similar to the structure shown in the [archetypes](06_extend-archetypes.md) section. + +:::tip + +Besides the basic project skeleton, the sample project also includes an example Dockerfile you can use to package your +application into a Docker container. + +::: + +Now you're ready to create your first data processor for StreamPipes! + +## Adding data processor requirements + +First, we will add a new stream requirement. +Create a new class `GeofencingProcessor` which should look as follows: + +```java +package org.apache.streampipes.pe.example; + +import org.apache.streampipes.extensions.api.pe.IStreamPipesDataProcessor; +import org.apache.streampipes.extensions.api.pe.config.IDataProcessorConfiguration; +import org.apache.streampipes.extensions.api.pe.context.EventProcessorRuntimeContext; +import org.apache.streampipes.extensions.api.pe.param.IDataProcessorParameters; +import org.apache.streampipes.extensions.api.pe.routing.SpOutputCollector; +import org.apache.streampipes.model.runtime.Event; +import org.apache.streampipes.sdk.builder.ProcessingElementBuilder; +import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder; +import org.apache.streampipes.sdk.builder.processor.DataProcessorConfiguration; +import org.apache.streampipes.sdk.helpers.EpProperties; +import org.apache.streampipes.sdk.helpers.EpRequirements; +import org.apache.streampipes.sdk.helpers.Labels; +import org.apache.streampipes.sdk.helpers.OutputStrategies; +import org.apache.streampipes.sdk.helpers.SupportedFormats; +import org.apache.streampipes.sdk.helpers.SupportedProtocols; +import org.apache.streampipes.vocabulary.SO; + +public class GeofencingProcessor implements IStreamPipesDataProcessor { + + private static final String LATITUDE_CENTER = "latitude-center"; + private static final String LONGITUDE_CENTER = "longitude-center"; + + + public IDataProcessorConfiguration declareConfig() { + return DataProcessorConfiguration.create( + GeofencingProcessor::new, + ProcessingElementBuilder.create( + "org.apache.streampipes.tutorial-geofencing" + ) + .category(DataProcessorType.ENRICH) + .withAssets(Assets.DOCUMENTATION, Assets.ICON) + .build()); + } + + @Override + public void onPipelineStarted(IDataProcessorParameters params, + SpOutputCollector collector, + EventProcessorRuntimeContext runtimeContext) { + + } + + @Override + public void onEvent(Event event, + SpOutputCollector collector) { + + } + + @Override + public void onPipelineStopped() { + + } +} + + +``` + +In this class, we need to implement three methods: The `declareConfig` method is used to define abstract stream +requirements such as event properties that must be present in any input stream that is later connected to the element +using the StreamPipes UI. +The second method, `onPipelineStarted` is triggered once a pipeline is started. +The `onEvent` method is called for every incoming event. +Finally, the `onPipelineStopped` method is called once the pipeline is stopped. + +Similar to data sources, the SDK provides a builder class to generate the description for data processors. + +The current code within the `declareConfig` method creates a new data processor with the ID. +The ID is used as the internal ID of the data processor, but also used to reference additional assets in the `resources` folder, such as a `strings.en` file, used to configure labels and description, and a `documentation.md` file, which will later servce as a markdown documentation in the UI. +But first, we will add some _stream requirements_ to the description. As we'd like to develop a generic pipeline element that +works with any event that provides a lat/lng pair, we define two stream requirements as stated below: + +```java +.requiredStream(StreamRequirementsBuilder + .create() + .requiredPropertyWithUnaryMapping( + EpRequirements.domainPropertyReq(Geo.LAT), + Labels.from("latitude-field","Latitude","The event property containing the latitude value"), + PropertyScope.MEASUREMENT_PROPERTY + ) + .requiredPropertyWithUnaryMapping( + EpRequirements.domainPropertyReq(Geo.LNG), + Labels.from("longitude-field","Longitude","The event property containing the longitude value"), + PropertyScope.MEASUREMENT_PROPERTY + ) + .build()) +``` + +The first line, `.requiredStream()` defines that we want a data processor with exactly one input stream. Adding more +stream requirements would create elements with multiple input connectors in StreamPipes. +Stream requirements can be assigned by using the `StreamRequirementsBuilder` class. +In our example, we define two requirements, so-called _domain property requirements_. In contrast to _data type +requirements_ where we'd expect an event property with a field of a specific data type (e.g., float), domain property +requirements expect a specific semantic type (called domain property), e.g., from a vocabulary such as the WGS84 Geo vocab. + +Once a pipeline is deployed, we are interested in the actual field (and its field name) that contains the latitude and +longitude values. +In some cases, there might be more than one field that satisfies a property requirement, and we would like users to +select the property the geofencing component should operate on. +Therefore, our example uses the method `requiredPropertyWithUnaryMapping`, which will map a requirement to a real event +property of an input stream and let the user choose the appropriate field in the StreamPipes UI when pipelines are +defined. + +Finally, the `PropertyScope` indicates that the required property is a measurement value (in contrast to a dimension +value). This allows us later to provide improved user guidance in the pipeline editor. + +Similar to mapping properties, text parameters have an internalId (radius), a label and a description. +In addition, we can assign a _value specification_ to the parameter indicating the value range we support. +Our example supports a radius value between 0 and 1000 with a granularity of 1. +In the StreamPipes UI, a required text parameter is rendered as a text input field, in case we provide an optional value +specification, a slider input is automatically generated. + +For now, we've assigned parameters with an internal ID, a label and a description. +To decouple human-readable labels and description from the actual data processor description, it is possible to extract the strings to a properties file. +In the `resources` folder, switch to a folder with the same name as the data processor's ID. If you've used the Maven archetype to build our project, there should be a `strings.en` file. +In this file, we can configure labels and descriptions. For instance, instead of writing + +```java + +.requiredPropertyWithUnaryMapping( + EpRequirements.domainPropertyReq(Geo.LAT), + Labels.from("latitude-field","Latitude","The event property containing the latitude value"), + PropertyScope.MEASUREMENT_PROPERTY + ) + +``` + +it is recommended to write + +```java + +.requiredPropertyWithUnaryMapping( + EpRequirements.domainPropertyReq(Geo.LAT), + Labels.withId("latitude-field"), + PropertyScope.MEASUREMENT_PROPERTY + ) + +``` + +and add the following line to the `strings.en` file: + +```properties + +latitude-field.title=Latitude +latitute-field.description=The event property containing the latitude value + +``` + +This feature will also ease future internationalization efforts. + +Besides requirements, users should be able to define the center coordinate of the Geofence and the size of the fence +defined as a radius around the center in meters. +The radius can be defined by adding a simple required text field to the description: + +```java +.requiredIntegerParameter("radius","Geofence Size","The size of the circular geofence in meters.",0,1000,1) +``` + +Such user-defined parameters are called _static properties_. There are many different types of static properties (see +the [Processor SDK](06_extend-sdk-static-properties.md) for an overview). Similar to stream requirements, it is also recommended to type `Labels.withId("radius")` and move labels and descriptions to the resource file. + +In this example, we'll further add two very simple input fields to let users provide latitude and longitude of the +geofence center. + +Add the following line to the `declareConfig` method: + +```java + .requiredFloatParameter(Labels.from(LATITUDE_KEY,"Latitude","The latitude value")) + .requiredFloatParameter(Labels.from(LONGITUDE_KEY,"Longitude","The longitude value")) + +``` + +Now we need to define the output of our Geofencing pipeline element. +As explained in the first section, the element should fire every time some geo-located entity arrives within the defined +geofence. +Therefore, the processor outputs the same schema as it receives as an input. +Although we don't know the exact input right now as it depends on the stream users connect in StreamPipes when creating +pipelines, we can define an _output strategy_ as follows: + +```java +.outputStrategy(OutputStrategies.keep()) +``` + +This defines a _KeepOutputStrategy_, i.e., the input event schema is not modified by the processor. +There are many more output strategies you can define depending on the functionality you desire, e.g., _AppendOutput_ for +defining a processor that enriches events or _CustomOutput_ in case you would like users to select the output by +themselves. + +That's it! We've now defined input requirements, required user input and an output strategy. +In the next section, you will learn how to extract these parameters once the pipeline element is invoked after a +pipeline was created. + +## Pipeline element invocation + +Once users start a pipeline that uses our geofencing component, the _onPipelineStarted_ method in our class is called. The +interface `IDataProcessorParameters` includes convenient access to user-configured parameters a users has selected in the pipeline +editor and information on the actual streams that are connected to the pipeline element. + +Next, we are interested in the fields of the input event stream that contains the latitude and longitude value we would +like to compute against the geofence center location as follows: + +```java + String latitudeFieldName = params.extractor().mappingPropertyValue("latitude-field"); + String longitudeFieldName = params.extractor().mappingPropertyValue("longitude-field"); +``` + +We use the same `internalId` we've used to define the mapping property requirements in the `declareModel` method. + +Next, for extracting the geofence center coordinates, add to class variables centerLatitude and centerLongitude and +assign the selected values using the following statements: + +```java + this.centerLatitude = params.extractor().singleValueParameter(LATITUDE_CENTER,Float.class); + this.centerLongitude = params.extractor().singleValueParameter(LONGITUDE_CENTER,Float.class); +``` + +The radius value can be extracted as follows: + +```java + int radius = params.extractor().singleValueParameter("radius",Float.class); +``` + +Great! That's all we need to describe a data processor for usage in StreamPipes. Your processor class should look as +follows: + +```java + +package org.apache.streampipes.pe.example; + +import org.apache.streampipes.extensions.api.pe.IStreamPipesDataProcessor; +import org.apache.streampipes.extensions.api.pe.config.IDataProcessorConfiguration; +import org.apache.streampipes.extensions.api.pe.context.EventProcessorRuntimeContext; +import org.apache.streampipes.extensions.api.pe.param.IDataProcessorParameters; +import org.apache.streampipes.extensions.api.pe.routing.SpOutputCollector; +import org.apache.streampipes.model.runtime.Event; +import org.apache.streampipes.sdk.builder.ProcessingElementBuilder; +import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder; +import org.apache.streampipes.sdk.builder.processor.DataProcessorConfiguration; +import org.apache.streampipes.sdk.helpers.EpProperties; +import org.apache.streampipes.sdk.helpers.EpRequirements; +import org.apache.streampipes.sdk.helpers.Labels; +import org.apache.streampipes.sdk.helpers.OutputStrategies; +import org.apache.streampipes.sdk.helpers.SupportedFormats; +import org.apache.streampipes.sdk.helpers.SupportedProtocols; +import org.apache.streampipes.vocabulary.SO; + +public class GeofencingProcessor implements IStreamPipesDataProcessor { + + private static final String LATITUDE_CENTER = "latitude-center"; + private static final String LONGITUDE_CENTER = "longitude-center"; + + private float centerLatitude; + private float centerLongitude; + private String latitudeFieldName; + private String longitudeFieldName; + + private int radius; + + public IDataProcessorConfiguration declareConfig() { + return DataProcessorConfiguration.create( + GeofencingProcessor::new, + ProcessingElementBuilder.create("org.streampipes.tutorial-geofencing") + .category(DataProcessorType.ENRICH) + .withAssets(Assets.DOCUMENTATION, Assets.ICON) + .withLocales(Locales.EN) + .requiredStream(StreamRequirementsBuilder + .create() + .requiredPropertyWithUnaryMapping(EpRequirements.domainPropertyReq(Geo.lat), + Labels.from("latitude-field", "Latitude", "The event " + + "property containing the latitude value"), PropertyScope.MEASUREMENT_PROPERTY) + .requiredPropertyWithUnaryMapping(EpRequirements.domainPropertyReq(Geo.lng), + Labels.from("longitude-field", "Longitude", "The event " + + "property containing the longitude value"), PropertyScope.MEASUREMENT_PROPERTY) + .build()) + .outputStrategy(OutputStrategies.keep()) + .requiredIntegerParameter("radius", "Geofence Size", "The size of the circular geofence in meters.", 0, 1000, 1) + .requiredFloatParameter(Labels.from(LATITUDE_CENTER, "Latitude", "The latitude value")) + .requiredFloatParameter(Labels.from(LONGITUDE_CENTER, "Longitude", "The longitude value")) + .build() + ); + } + + @Override + public void onPipelineStarted(IDataProcessorParameters params, + SpOutputCollector collector, + EventProcessorRuntimeContext runtimeContext) { + this.centerLatitude = params.extractor().singleValueParameter(LATITUDE_CENTER, Float.class); + this.centerLongitude = params.extractor().singleValueParameter(LONGITUDE_CENTER, Float.class); + this.latitudeFieldName = params.extractor().mappingPropertyValue("latitude-field"); + this.longitudeFieldName = params.extractor().mappingPropertyValue("longitude-field"); + this.radius = params.extractor().singleValueParameter("radius", Integer.class); + } + + @Override + public void onEvent(Event event, + SpOutputCollector collector) { + + } + + @Override + public void onPipelineStopped() { + + } +} + +``` + +## Adding an implementation + +Everything we need to do now is to add an implementation. + +Add the following piece of code to the onEvent method, which realizes the Geofencing functionality: + +```java + + @Override + public void onEvent(Event event, + SpOutputCollector collector) { + float latitude = event.getFieldBySelector(latitudeFieldName).getAsPrimitive().getAsFloat(); + float longitude = event.getFieldBySelector(longitudeFieldName).getAsPrimitive().getAsFloat(); + + float distance = distFrom(latitude,longitude, centerLatitude, centerLongitude); + + if(distance <= radius){ + collector.collect(event); + } + } + + public static float distFrom(float lat1, float lng1, float lat2, float lng2) { + double earthRadius = 6371000; + double dLat = Math.toRadians(lat2-lat1); + double dLng = Math.toRadians(lng2-lng1); + double a = Math.sin(dLat/2)*Math.sin(dLat/2) + + Math.cos(Math.toRadians(lat1))*Math.cos(Math.toRadians(lat2)) * + Math.sin(dLng/2)*Math.sin(dLng/2); + + double c = 2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a)); + + return(float)(earthRadius*c); + } +``` + +We won't go into details here as this isn't StreamPipes-related code, but in general the class extracts latitude and +longitude fields from the input event (which is provided as a map data type) and calculates the distance between the +geofence center and these coordinates. +If the distance is below the given radius, the event is forwarded to the next operator. + +See the [event model](06_extend-sdk-event-model.md) guide to learn how to extract parameters from events. + +## Registering the pipeline element + +The final step is to register the data processor in the `Init` method. Add the following line to +the `SpServiceDefinitionBuilder`: + +```java + .registerPipelineElement(new GeofencingProcessor()) +``` + +## Starting the service + +:::tip + +Once you start the service, it will register in StreamPipes with the hostname. The hostname will be auto-discovered and +should work out-of-the-box. +In some cases, the detected hostname is not resolvable from within a container (where the core is running). In this +case, provide a SP_HOST environment variable to override the auto-discovery. + +::: + +:::tip + +The default port of all pipeline element services as defined in the `create` method is port 8090. +If you'd like to run multiple services at the same time on your development machine, change the port here. As an +alternative, you can also provide an env variable `SP_PORT` which overrides the port settings. This is useful to use +different configs for dev and prod environments. + +::: + +Now we are ready to start our service! + +Configure your IDE to provide an environment variable called ``SP_DEBUG`` with value ``true`` when starting the project. + +Execute the main method in the class `Init` we've just created. + +The service automatically registers itself in StreamPipes. +To install the just created element, open the StreamPipes UI and follow the manual provided in +the [user guide](03_use-install-pipeline-elements.md). + +## Read more + +Congratulations! You've just created your first data processor for StreamPipes. +There are many more things to explore and data processors can be defined in much more detail using multiple wrappers. +Follow our [SDK guide](06_extend-sdk-static-properties.md) to see what's possible! diff --git a/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-sinks.md b/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-sinks.md new file mode 100644 index 000000000..09baeff71 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/06_extend-tutorial-data-sinks.md @@ -0,0 +1,272 @@ +--- +id: extend-tutorial-data-sinks +title: "Tutorial: Data Sinks" +sidebar_label: "Tutorial: Data Sinks" +--- + +In this tutorial, we will add a new data sink using the standalone wrapper. + +From an architectural point of view, we will create a self-contained service that includes the description of the data +sink and a corresponding implementation. + +## Objective + +We are going to create a new data sink that calls an external HTTP endpoint to forward data to an external service. + +For each incoming event, an external service is invoked using an HTTP POST request. In this example, we'll call an +endpoint provided by [RequestBin](https://requestbin.com/). +To setup your own endpoint, go to [https://requestbin.com/](https://requestbin.com/) and click "Create a request bin". +Copy the URL of the newly created endpoint. + +## Project setup + +Instead of creating a new project from scratch, we recommend to use the Maven archetype to create a new project +skeleton (streampipes-archetype-extensions-jvm). +Enter the following command in a command line of your choice (Apache Maven needs to be installed): + +``` +mvn archetype:generate -DarchetypeGroupId=org.apache.streampipes \ +-DarchetypeArtifactId=streampipes-archetype-extensions-jvm -DarchetypeVersion=0.93.0 \ +-DgroupId=org.streampipes.tutorial -DartifactId=sink-tutorial -DclassNamePrefix=Rest -DpackageName=mypackage +``` + +You will see a project structure similar to the structure shown in the [archetypes](06_extend-archetypes.md) section. + +:::tip + +Besides the basic project skeleton, the sample project also includes an example Dockerfile you can use to package your +application into a Docker container. + +::: + +Now you're ready to create your first data sink for StreamPipes! + +## Adding data sink requirements + +First, we will add a new stream requirement. +Create a class `RestSink` which should look as follows: + +```java +package org.apache.streampipes.pe.example; + +import org.apache.streampipes.extensions.api.pe.IStreamPipesDataSink; +import org.apache.streampipes.extensions.api.pe.config.IDataSinkConfiguration; +import org.apache.streampipes.extensions.api.pe.context.EventSinkRuntimeContext; +import org.apache.streampipes.extensions.api.pe.param.IDataSinkParameters; +import org.apache.streampipes.model.DataSinkType; +import org.apache.streampipes.model.runtime.Event; +import org.apache.streampipes.model.schema.PropertyScope; +import org.apache.streampipes.sdk.builder.DataSinkBuilder; +import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder; +import org.apache.streampipes.sdk.builder.sink.DataSinkConfiguration; +import org.apache.streampipes.sdk.helpers.EpRequirements; +import org.apache.streampipes.sdk.helpers.Labels; +import org.apache.streampipes.sdk.helpers.Locales; +import org.apache.streampipes.sdk.utils.Assets; + +public class RestSink implements IStreamPipesDataSink { + + @Override + public IDataSinkConfiguration declareConfig() { + return DataSinkConfiguration.create( + RestSink::new, + DataSinkBuilder.create("org.apache.streampipes.tutorial.pe.sink.rest") + .category(DataSinkType.NOTIFICATION) + .withAssets(Assets.DOCUMENTATION, Assets.ICON) + .withLocales(Locales.EN) + .requiredStream(StreamRequirementsBuilder + .create() + .requiredPropertyWithNaryMapping(EpRequirements.anyProperty(), Labels.withId( + "fields-to-send"), PropertyScope.NONE) + .build()) + .build() + ); + } + + @Override + public void onPipelineStarted(IDataSinkParameters params, + EventSinkRuntimeContext eventSinkRuntimeContext) { + + } + + @Override + public void onEvent(Event event) { + + } + + @Override + public void onPipelineStopped() { + + } + + +``` + +In this class, we need to implement three methods: The `declareConfig` method is used to define abstract stream +requirements such as event properties that must be present in any input stream that is later connected to the element +using the StreamPipes UI. +The second method, `onPipelineStarted` is called once a pipeline using this sink is started. The third method, `onEvent`, is +called for every incoming event. + +The `DataSinkBuilder` within the ``declareConfig`` method describes the properties of our data sink: + +* ``category`` defines a category for this sink. +* ``withAssets`` denotes that we will provide an external documentation file and an icon, which can be found in + the ``resources`` folder +* ``withLocales`` defines that we will provide an external language file, also available in the ``resources`` folder +* ``requiredStream`` defines requirements any input stream connected to this sink must provide. In this case, we do not + have any specific requirements, we just forward all incoming events to the REST sink. However, we want to let the user + display a list of available fields from the connected input event, where users can select a subset. This is defined by + defining a Mapping from the empty requirement. This will later on render a selection dialog in the pipeline editor. + +The ``onPipelineStarted`` method is called when a pipeline containing the sink is started. Once a pipeline is started, we +would like to extract user-defined parameters. +In this example, we simply extract the fields selected by users that should be forwarded to the REST sink. Finally, we +return a new configured event sink containing the parameters. + +## Pipeline element invocation + +Once users start a pipeline that uses our geofencing component, the _onInvocation_ method in our class is called. The +interface `IDataSinkParameters` includes methods to extract the configuration parameters a user has selected in +the pipeline editor and information on the actual streams that are connected to the pipeline element. + +## Adding an implementation + +Now we'll add a proper implementation (i.e., the Rest call executed for every incoming event) to the following methods: + +Our final class should look as follows: + +```java +package org.apache.streampipes.pe.example; + +import org.apache.streampipes.commons.exceptions.SpRuntimeException; +import org.apache.streampipes.dataformat.SpDataFormatDefinition; +import org.apache.streampipes.dataformat.json.JsonDataFormatDefinition; +import org.apache.streampipes.extensions.api.pe.IStreamPipesDataSink; +import org.apache.streampipes.extensions.api.pe.config.IDataSinkConfiguration; +import org.apache.streampipes.extensions.api.pe.context.EventSinkRuntimeContext; +import org.apache.streampipes.extensions.api.pe.param.IDataSinkParameters; +import org.apache.streampipes.model.DataSinkType; +import org.apache.streampipes.model.runtime.Event; +import org.apache.streampipes.model.schema.PropertyScope; +import org.apache.streampipes.sdk.builder.DataSinkBuilder; +import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder; +import org.apache.streampipes.sdk.builder.sink.DataSinkConfiguration; +import org.apache.streampipes.sdk.helpers.EpRequirements; +import org.apache.streampipes.sdk.helpers.Labels; +import org.apache.streampipes.sdk.helpers.Locales; +import org.apache.streampipes.sdk.utils.Assets; + +import com.google.common.base.Charsets; +import org.apache.http.client.fluent.Request; +import org.apache.http.entity.StringEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class RestSink implements IStreamPipesDataSink { + + private static final Logger LOG = LoggerFactory.getLogger(RestSink.class); + + private static final String REST_ENDPOINT_URI = YOUR_REQUEST_BIN_URL; + private List fieldsToSend; + private SpDataFormatDefinition dataFormatDefinition; + + @Override + public IDataSinkConfiguration declareConfig() { + return DataSinkConfiguration.create( + RestSink::new, + DataSinkBuilder.create("org.apache.streampipes.tutorial.pe.sink.rest") + .category(DataSinkType.NOTIFICATION) + .withAssets(Assets.DOCUMENTATION, Assets.ICON) + .withLocales(Locales.EN) + .requiredStream(StreamRequirementsBuilder + .create() + .requiredPropertyWithNaryMapping(EpRequirements.anyProperty(), Labels.withId( + "fields-to-send"), PropertyScope.NONE) + .build()) + .build() + ); + } + + @Override + public void onPipelineStarted(IDataSinkParameters params, + EventSinkRuntimeContext eventSinkRuntimeContext) { + this.dataFormatDefinition = new JsonDataFormatDefinition(); + this.fieldsToSend = params.extractor().mappingPropertyValues("fields-to-send"); + } + + @Override + public void onEvent(Event event) { + Map outEventMap = event.getSubset(fieldsToSend).getRaw(); + try { + String json = new String(dataFormatDefinition.fromMap(outEventMap)); + Request.Post(REST_ENDPOINT_URI).body(new StringEntity(json, Charsets.UTF_8)).execute(); + } catch (SpRuntimeException e) { + LOG.error("Could not parse incoming event"); + } catch (IOException e) { + LOG.error("Could not reach endpoint at {}", REST_ENDPOINT_URI); + } + } + + @Override + public void onPipelineStopped() { + + } +} + +``` + +The only class variable you need to change right now is the REST_ENDPOINT_URL. Change this url to the URL provided by +your request bin. +In the ``ònEvent`` method, we use a helper method to get a subset of the incoming event. +Finally, we convert the resulting ``Map`` to a JSON string and call the endpoint. + +## Preparing the service + +The final step is to register the sink as a pipeline element. + +Go to the class `Init` and register the sink: + +```java +.registerPipelineElement(new RestSink()) +``` + +## Starting the service + +:::tip + +Once you start the service, it will register in StreamPipes with the hostname. The hostname will be auto-discovered and +should work out-of-the-box. +In some cases, the detected hostname is not resolvable from within a container (where the core is running). In this +case, provide a SP_HOST environment variable to override the auto-discovery. + +::: + +:::tip + +The default port of all pipeline element services as defined in the `create` method is port 8090. +If you'd like to run multiple services at the same time on your development machine, change the port here. As an +alternative, you can also provide an env variable `SP_PORT` which overrides the port settings. This is useful to use +different configs for dev and prod environments. + +::: + +Now we are ready to start our service! + +Configure your IDE to provide an environment variable called ``SP_DEBUG`` with value ``true`` when starting the project. + +Execute the main method in the class `Init` we've just created. The service automatically registers itself in +StreamPipes. + +To install the created element, open the StreamPipes UI and follow the manual provided in +the [user guide](03_use-install-pipeline-elements.md). + +## Read more + +Congratulations! You've just created your first data sink for StreamPipes. +There are many more things to explore and data sinks can be defined in much more detail using multiple wrappers. +Follow our [SDK guide](../dev-guide-sdk-guide-sinks) to see what's possible! diff --git a/website-v2/versioned_docs/version-0.95.0/07_technicals-architecture.md b/website-v2/versioned_docs/version-0.95.0/07_technicals-architecture.md new file mode 100644 index 000000000..e62ba4625 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/07_technicals-architecture.md @@ -0,0 +1,110 @@ +--- +id: technicals-architecture +title: Architecture +sidebar_label: Architecture +--- + +## Architecture + +StreamPipes Architecture + +Apache StreamPipes implements a microservice architecture as shown in the figure above. + +## StreamPipes Core + +The StreamPipes Core is the central component to manage all StreamPipes resources. +It delegates the management of adapters, pipeline elements, pipelines and functions to registered extensions services (see below) and monitors the execution of extensions. +The Core also provides internal REST interfaces to communicate with the user interface, as well as public REST interfaces that can be used by external applications and StreamPipes clients. + +Configuration and user data are stored in an Apache CouchDB database. + +## StreamPipes Extensions + +An Apache StreamPipes extensions service is a microservice which contains the implementation of specific adapters, data streams, data processors, data sinks and functions. +Multiple extension services can be part of a single StreamPipes installation. +Each service might provide its own set of extensions. Extensions services register at the StreamPipes Core at startup. Users are able to install all or a subset of extensions of each service. +This allows StreamPipes to be extended at runtime by starting a new service with additional extensions. + +Extensions can be built using the SDK (see [Extending StreamPipes](06_extend-setup.md)). +Extensions services can be provided either in Java or in Python. + +:::info + +As of version 0.93.0, the Python SDK supports functions only. If you would like to develop pipeline elements in Python as well, let us know in a [Github discussions](https://github.com/apache/streampipes/discussions) comment, so that we can better prioritize development. + +::: + + +An extensions service interacts with the core by receiving control messages to invoke or detach an extension. +In addition, the core regularly fetches monitoring and log data from each registered extensions service. + + +## StreamPipes Client + +The Apache StreamPipes Client is a lightweight library for Java and Python which can be used to interact with StreamPipes resources programmatically. +For instance, users use the client to influence the control flow of pipelines, to download raw data from the data lake APIs or to realize custom applications with live data. + + +## Third-party systems + +In addition to the core components, an Apache StreamPipes version uses several third-party services, which are part of the standard installation. + +* Configurations and user data is stored in an [Apache CouchDB](https://couchdb.apache.org) database. +* Time-series data is stored in an [InfluxDB](https://github.com/influxdata/influxdb) database. +* Events are exchanged over a messaging system. Users can choose from various messaging systems that StreamPipes supports. Currently, we support [Apache Kafka](https://kafka.apache.org), [Apache Pulsar](https://pulsar.apache.org), [MQTT](https://mqtt.org/) and [NATS](https://nats.io/). The selection of the right messaging system depends on the use case. See [Messaging](07_technicals-messaging.md) for more information. + +:::info + +Versions prior to 0.93.0 included Consul for service discovery and registration. Starting from 0.93.0 onwards, we switched to an internal service discovery mechanism. + +::: + +All mentioned third-party services are part of the default installation and are auto-configured during the installation process. + +## Programming Languages + +Apache StreamPipes is mainly written in Java. +Services are based on Spring Boot. +The included [Python integration](https://streampipes.apache.org/docs/docs/python/latest/) is written in Python. + +The user interface is mainly written in TypeScript using the Angular framework. + + +## Data Model + +Internally, Apache StreamPipes realizes a stream processing layer where events are continuously exchanged over a messaging system. +When building a pipeline, data processors consume data from a topic assigned by the core and publish data back to another topic, which is also assigned by the core. + +At runtime, events have a flat and easily understandable data structure, consisting of key/value pairs. Events are serialized in JSON, although StreamPipes can be configured to use other (binary) message formats. + +This allows for easy integration with other systems which want to consume data from Streampipes, since an event could look as simple as this: + +```json +{ + "timestamp": 1234556, + "deviceId": "ABC", + "temperature": 37.5 +} +``` + +However, this wouldn't be very expressive, right? To [assist users](07_technicals-user-guidance.md), StreamPipes provides a rich description layer for events. So under the hood, for the `temperature` field shown above StreamPipes can also store the following: + +```json +{ + "label": "Temperature", + "description": "Measures the temperature during leakage tests", + "measurementUnit": "https://qudt.org/vocab/unit/DEG_C", + "runtimeName": "temperature", + "runtimeType": "xsd:float", + "semanticType": "https://my-company-vocabulary/leakage-test-temperature" +} +``` + +By dividing the description layer from the runtime representation, we get a good trade-off between expressivity, readability for humans and lightweight runtime message formats. +The schema is stored in an internal schema registry and available to the client APIs and user interface views to improve validation and user guidance. + +StreamPipes also supports arrays and nested structures, although we recommend using flat events where possible to ease integration with downstream systems (such as time-series storage). + + + + diff --git a/website-v2/versioned_docs/version-0.95.0/07_technicals-messaging.md b/website-v2/versioned_docs/version-0.95.0/07_technicals-messaging.md new file mode 100644 index 000000000..d5308a6d8 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/07_technicals-messaging.md @@ -0,0 +1,65 @@ +--- +id: technicals-messaging +title: Messaging +sidebar_label: Messaging +--- + +## Architecture + +To exchange messages at runtime between individual [Extensions Services](07_technicals-architecture.md), StreamPipes uses external messaging systems. +This corresponds to an event-driven architecture with a central message broker and decoupled services which consume and produce events from the messaging system. + +There are many different open source messaging systems on the market, which each have individual strengths. +To provide a flexible system which matches different needs, StreamPipes can be configured to use various messaging systems. + +## Supported messaging systems + +The following messaging systems are currently supported: + +* Apache Kafka +* Apache Pulsar +* MQTT +* NATS + +## Configure StreamPipes to use another messaging system + +Configuring StreamPipes for one of these messaging system is an installation-time configuration. +We currently do not recommend to change the configuration at runtime. + +The protocol can be configured with the environment variable `SP_PRIORITIZED_PROTOCOL` assigned to the core with one of the following values: + +```bash +SP_PRIORITIZED_PROTOCOL=kafka # Use Kafka as protocol +SP_PRIORITIZED_PROTOCOL=pulsar # Use Pulsar as protocol +SP_PRIORITIZED_PROTOCOL=mqtt # Use MQTT as protocol +SP_PRIORITIZED_PROTOCOL=nats # Use NATS as protocol +``` + +Note that each extension service can support an arbitrary number of protocols. For instance, you can have a lightweight extension service which only supports NATS, but have another, cloud-centered service which supports Kafka, both registered at the Core. +To select a protocol when multiple protocols are supported by two pipeline elements, StreamPipes selects a protocol based on a priority, which can be configured in the [Configuration View](03_use-configurations.md). +StreamPipes ensures that only pipeline elements which have a commonly supported protocol can be connected. + +Note that you might need to change the installation files. For the `Docker-Compose` based installation, we provide various compose file for different messaging setups. For the `Kubernetes` installation, we provide variables which can be set in the helm chart's `values.yaml` file. + +### Configure broker addresses + +By default, StreamPipes assumes that the messaging system is started from its own environment, e.g., the system configured in the selected `Docker-Compose` file. + +Besides that, it is also possible to let StreamPipes connect to an externally provided messaging system. For this purpose, various environment variables exist. + +* `SP_PRIORITIZED_PROTOCOL` to set the prioritized protocol to either `kafka`, `mqtt`, `nats` or `pulsar` + +* `SP_KAFKA_HOST`, `SP_KAFKA_PORT` to configure Kafka access +* `SP_MQTT_HOST`, `SP_MQTT_PORT` to configure MQTT access +* `SP_NATS_HOST`, `SP_NATS_PORT` to configure NATS access +* `SP_PULSAR_URL` to configure Pulsar access + + +Most settings can also be set in the UI under `Settings->Messaging`. + +:::warning Installation-time configurations +Although it is currently possible to change messaging settings in the user interface, we do not support dynamic modification of messaging systems. +Choosing a proper system is considered an installation-time setting which should not be changed afterwards. +Already existing Adapters and pipeline elements are not properly updated after changes of the messaging layer. +::: + diff --git a/website-v2/versioned_docs/version-0.95.0/07_technicals-runtime-wrappers.md b/website-v2/versioned_docs/version-0.95.0/07_technicals-runtime-wrappers.md new file mode 100644 index 000000000..9cebadfd2 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/07_technicals-runtime-wrappers.md @@ -0,0 +1,37 @@ +--- +id: technicals-runtime-wrappers +title: Runtime Wrappers +sidebar_label: Runtime Wrappers +--- + +## Overview + +In general, StreamPipes has an exchangeable runtime layer, e.g., the actual processing of incoming events can be delegated to a third-party stream processing system such as Kafka Streams or Apache Flink. + +The default runtime wrapper is the StreamPipes Native Wrapper, called the `StandaloneWrapper`. + +Although not recommended for production, we invite interested developers to check out our experimental wrappers: + +* Kafka Streams runtime wrapper at [https://github.com/apache/streampipes/tree/dev/streampipes-wrapper-kafka-streams](https://github.com/apache/streampipes/tree/dev/streampipes-wrapper-kafka-streams) +* Apache Flink runtime wrapper at [https://github.com/apache/streampipes/tree/dev/streampipes-wrapper-flink](https://github.com/apache/streampipes/tree/dev/streampipes-wrapper-flink) + +## Assigning a runtime wrapper to an extension service + +Runtime wrappers can be assigned in the `Service Definition` of the `Init` class of an extension service: + +```java + + @Override + public SpServiceDefinition provideServiceDefinition(){ + return SpServiceDefinitionBuilder.create("org.apache.streampipes.extensions.all.jvm", + "StreamPipes Extensions (JVM)", + "",8090) + ... + .registerRuntimeProvider(new StandaloneStreamPipesRuntimeProvider()) + ... + .build(); + } + +``` + +Please let us know through our communication channels if you are interested in this feature and if you are willing to contribute! diff --git a/website-v2/versioned_docs/version-0.95.0/07_technicals-user-guidance.md b/website-v2/versioned_docs/version-0.95.0/07_technicals-user-guidance.md new file mode 100644 index 000000000..697411861 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/07_technicals-user-guidance.md @@ -0,0 +1,7 @@ +--- +id: technicals-user-guidance +title: User Guidance +sidebar_label: User Guidance +--- + +tbd \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/08_debugging.md b/website-v2/versioned_docs/version-0.95.0/08_debugging.md new file mode 100644 index 000000000..95892c175 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/08_debugging.md @@ -0,0 +1,7 @@ +--- +id: debugging-debugging +title: Debugging +sidebar_label: Debugging +--- + +tbd \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/08_monitoring.md b/website-v2/versioned_docs/version-0.95.0/08_monitoring.md new file mode 100644 index 000000000..6680b5d86 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/08_monitoring.md @@ -0,0 +1,7 @@ +--- +id: debugging-monitoring +title: Monitoring +sidebar_label: Monitoring +--- + +tbd \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/09_contribute.md b/website-v2/versioned_docs/version-0.95.0/09_contribute.md new file mode 100644 index 000000000..119568929 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/09_contribute.md @@ -0,0 +1,17 @@ +--- +id: community-contribute +title: Contribute +sidebar_label: Contribute +--- + +## Contribute + +We welcome contributions to StreamPipes. If you are interested in contributing to StreamPipes, let us know! You'll +get to know an open-minded and motivated team working together to build the next IIoT analytics toolbox. + +Here are some first steps in case you want to contribute: +* Subscribe to our dev mailing list [dev-subscribe@streampipes.apache.org](mailto:dev-subscribe@streampipes.apache.org) +* Send an email, tell us about your interests and which parts of Streampipes you'd like to contribute (e.g., core or UI)! +* Ask for a mentor who helps you to understand the code base and guides you through the first setup steps +* Find an issue on [GitHub](https://github.com/apache/streampipes/issues) which is tagged with a _good first issue_ tag +* Have a look at our **developer wiki** at [https://cwiki.apache.org/confluence/display/STREAMPIPES](https://cwiki.apache.org/confluence/display/STREAMPIPES) to learn more about StreamPipes development. diff --git a/website-v2/versioned_docs/version-0.95.0/09_get-help.md b/website-v2/versioned_docs/version-0.95.0/09_get-help.md new file mode 100644 index 000000000..077f0b62f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/09_get-help.md @@ -0,0 +1,25 @@ +--- +id: community-get-help +title: Get Help +sidebar_label: Get Help +--- + +The Apache StreamPipes community is happy to help with any questions or problems you might have. + +## Questions +Subscribe to our user mailing list to ask a question. + +[Mailing Lists](https://streampipes.apache.org/mailinglists.html) + +To subscribe to the user list, send an email to [users-subscribe@streampipes.apache.org](users-subscribe@streampipes.apache.org) + +You can also ask questions on our Github discussions page: +[Github Discussions](https://github.com/apache/streampipes/discussions) + +## Bugs and Feature Requests + +If you've found a bug or have a feature that you'd love to see in StreamPipes, feel free to create an issue on [GitHub](https://github.com/apache/streampipes/issues) +or [discuss your ideas](https://github.com/apache/streampipes/discussions/categories/ideas). + + + diff --git a/website-v2/versioned_docs/version-0.95.0/faq-common-problems.md b/website-v2/versioned_docs/version-0.95.0/faq-common-problems.md new file mode 100644 index 000000000..14195c0f0 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/faq-common-problems.md @@ -0,0 +1,73 @@ +--- +id: faq-common-problems +title: Common Problems +sidebar_label: Common Problems +--- + +* Windows 10: Consul, Kafka, Zookeeper, or Kafka-Rest did not start +* Linux / OSX: Consul does not start +* Run StreamPipes in a VM in Windows +* Only few processors are available in the pipeline editor +* No data is shown in the live dashbord +* Windows 10: Should I use settings windows containers or docker containers? +* Configurations are not deleted +* Help us to improve StreamPipes and this documentation +* Docker Network already used + +## Windows 10: Consul, Kafka, Zookeeper, or Kafka-Rest did not start +**Problem:** You get an error message similar to: `ERROR: for consul Cannot start service consul: b'driver failed programming external connectivity on endpoint sp-test_consul_1 (eae0457fc03c1364b8e81a6e155ca4b95ee1e1d01bb3c1aa9dd5192bdcb7b91a): Error starting userland proxy: mkdir /port/tcp:0.0.0.0:8600:tcp:172.30.0.9:8600: input/output error` + +**Solution:** To resolve this problem, stop StreamPipes with `streampipes stop` and restart Docker via the Docker settings in the task bar. +After Docker was restarted, run `streampipes start`. + +## Consul does not start +**Problem:** After starting StreamPipes with `streampipes start`, there is an error with Consul: + +**Solution:** To resolve this, execute `streampipes stop`, wait a minute and start it again with `streampipes start`. If you've installed an old version of StreamPipes (before the installer was available), make sure that no network suffixed with `spnet` exists in Docker. Type `docker network ls` to check and `docker network rm NETWORK_NAME` to remove the existing network before running the installer. + +## Run StreamPipes in a VM in Windows +**Problem:** StreamPipes does not work properly with Docker under Windows 8 or earlier versions. + +**Solution:** We do support virtual machines (VMs), but if you run them under Windows, there might be problems with docker and its network configurations. +Please use Windows 10, OSX or Linux. +You can also use a VM from a cloud provider to test StreamPipes. + + +## Only few processors are available in the pipeline editor +**Problem:** In the Pipeline Editor, only a few processors can be used in pipelines. + +**Solution:** In the demo/desktop version, we only integrated a few processors. To ensure that you can easily try out StreamPipes (even on your laptop), + we tried to make it as lightweight as possible. If you are interested in more sophisticated algorithms, pleas contact us. + + +## No data is shown in the live dashboard +**Problem:** The live dashboard does not show any data. + +**Solution:** If this is the case, your IP is probably configured wrong. +You can reinstall the system by running `streampipes clean` and then `streampipes start` again. +This will delete all StreamPipes configurations. StreamPipes is designed as a server application and requires a fixed IP. +We created a version to easily run it on your laptop and test it, but on your laptop you usually get a new IP when you change the network. +This problem only occurs in testing scenarios, in production scenarios the IP can also be changed manually without data loss. + +## Windows 10: Should I use settings windows containers or docker containers +**Problem:** StreamPipes does not work with Windows 10. + +**Solution:** You should use docker containers. Go to the docker settings on our taks bar and select 'Switch to Docker containers'. + +## Configurations are not deleted +**Problem:** The configurations are not deleted from the host system. Even after manually removing the 'config/' folder StreamPipes settings are note deleted. +Also the Consul settings are still there. + +**Solution:** Probably Docker did not mount a volume in the 'config/' folder. You must delete the anonymous docker volumes manually. See in docker [documentation](https://docs.docker.com/engine/reference/commandline/volume_rm/). + + +## Docker Network already used +**Problem:** When starting StreamPipes the error message: "Creating network 'streampipes-cli_spnet' with driver 'bridge' Pool overlaps with other one on this address space" is shown. + +**Solution:** Delete old networks for example with "docker network prune". + +## Help us to improve StreamPipes and this documentation +Help us to improve this section. +If you have any problems with the system or with the documentation, do not hesitate to contact us. +Our goal is to continuously improve StreamPipes. +Your help and feedback is welcome. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.image.stream.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.image.stream.md new file mode 100644 index 000000000..a03806d9e --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.image.stream.md @@ -0,0 +1,38 @@ +--- +id: org.apache.streampipes.connect.adapters.image.stream +title: Image Upload (Stream) +sidebar_label: Image Upload (Stream) +--- + + + + + +

+ +

+ +*** + +## Description + +Upload a zip file of images and create an event per image + +*** + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.iss.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.iss.md new file mode 100644 index 000000000..ab27d577d --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.adapters.iss.md @@ -0,0 +1,39 @@ +--- +id: org.apache.streampipes.connect.adapters.iss +title: ISS Location +sidebar_label: ISS Location +--- + + + + + +

+ +

+ +*** + +## Description + +Shows the live position of the International Space Station (ISS), updated every two seconds. + + +*** + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.influxdb.stream.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.influxdb.stream.md new file mode 100644 index 000000000..d01a6ac50 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.influxdb.stream.md @@ -0,0 +1,41 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.influxdb.stream +title: InfluxDB Stream Adapter +sidebar_label: InfluxDB Stream Adapter +--- + + + + + +

+ +

+ +*** + +## Description +Creates a data stream for an InfluxDB measurement. + +*** + +## Configuration + + + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.iolink.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.iolink.md new file mode 100644 index 000000000..38363c96e --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.iolink.md @@ -0,0 +1,90 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.iolink +title: ifm IOLink +sidebar_label: ifm IOLink +--- + + + + + +

+ +

+ +*** + +## Description + +This adapter enables the integration of IO-Link sensor data produced by an ifm IO-Link Master +(e.g., AL1350) with Apache StreamPipes. To use this adapter, you need to configure your IO-Link +master to publish events to an MQTT broker. This can be achieved through a REST interface or via +the browser at `http://##IP_OF_IO_LINK_MASTER##/web/subscribe`. For detailed instructions, +please refer to the ifm documentation. + +### Requirements +The JSON events should include the following information: +- `deviceinfo.serialnumber` +- Only the pdin value is required for each port (e.g., `port[0]`). +- The event `timer[1].datachanged` can be used as a trigger. +Using this adapter, you can create a stream for sensors of the same type. + +### Restrictions +This version supports a single IO-Link master. If you want to connect multiple masters, they must have the same setup. +If you have different requirements, please inform us through the mailing list or GitHub discussions. + +*** + +## Configuration + +Here is a list of the configuration parameters you must provide. + +### Broker URL + +Enter the URL of the broker, including the protocol (e.g. `tcp://10.20.10.3:1883`) + +### Access Mode + +If necessary, provide broker credentials. + +### Ports + +Select the ports that are connected to the IO-Link sensors. + +### Sensor Type + +Choose the type of sensor you want to connect. (**IMPORTANT:** Currently, only the VVB001 is supported) + +## Output + +The output includes all values from the selected sensor type. Here is an example for the `VVB001 sensor`: +``` +{ + "aPeak": 6.6, + "aRms": 1.8, + "crest": 3.7, + "out1": true, + "out2": true, + "port": "000000001234", + "status": 0, + "temperature": 22, + "timestamp": 1685525380729, + "vRms": 0.0023 +} +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.mqtt.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.mqtt.md new file mode 100644 index 000000000..80cb04156 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.mqtt.md @@ -0,0 +1,64 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.netio.mqtt +title: NETIO MQTT M2M +sidebar_label: NETIO MQTT M2M +--- + + + + + +

+ +

+ +*** + +## Description + +Connect Robots running on ROS + + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +Describe the configuration parameters here + +### Ros Bridge + +Example: test-server.com (No protocol) + +### Port + +The port of the ROS instance. + +### Topic + +Example: /battery (Starts with /) + + +## Output + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.rest.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.rest.md new file mode 100644 index 000000000..83c741ecc --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.netio.rest.md @@ -0,0 +1,64 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.netio.rest +title: NETIO http JSON +sidebar_label: NETIO http JSON +--- + + + + + +

+ +

+ +*** + +## Description + +Connect Robots running on ROS + + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +Describe the configuration parameters here + +### Ros Bridge + +Example: test-server.com (No protocol) + +### Port + +The port of the ROS instance. + +### Topic + +Example: /battery (Starts with /) + + +## Output + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.oi4.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.oi4.md new file mode 100644 index 000000000..1621d8702 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.oi4.md @@ -0,0 +1,88 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.oi4 +title: OI4 +sidebar_label: OI4 +--- + + + +# Open Industry 4.0 (OI4) + +

+ +

+ +--- + + + +The OI4 adapter facilitates the integration of any OT-device compliant with the OI4 standard into Apache StreamPipes. +For detailed information about this standard, please refer to their [development guide](https://openindustry4.com/fileadmin/Dateien/Downloads/OEC_Development_Guideline_V1.1.1.pdf). + +### Requirements + +Your OI4-compatible device should emit data via an MQTT broker. + +### Restrictions + +This adapter exclusively allows data consumption from a specific MQTT topic. +If you have different requirements, please notify us through the mailing list or GitHub discussions. + +--- + +## Configuration + +Below is a list of the configuration parameters you need to provide. + +### Broker URL + +Enter the URL of the broker, including the protocol and port number (e.g., `tcp://10.20.10.3:1883`). + +### Access Mode + +Choose between unauthenticated access or input your credentials for authenticated access. + +### Sensor Description + +You should provide information about the sensor you want to connect to. This can be achieved in two ways: + +a) **By Type**: Specify the type of sensor you want to connect to, e.g., `'VVB001'`. <\br> +b) **By IODD**: Simply upload the IODD description of the respective sensor. Please note: This feature is not yet available! If you're interested in this feature, please notify us through the mailing list or GitHub discussions and share your use case with us. + +### Selected Sensors + +Configure which sensors of the master device you want to connect to. You can either select `All`, which will provide data from all sensors available on the respective MQTT topic, or choose `Custom Selection` and provide a list of sensor IDs in a comma-separated string (e.g., `000008740649,000008740672`). + +## Output + +The output consists of all values from the selected sensor type. Below is an example for the `VVB001 sensor`: + +```json +{ + "a-Rms": 1.8, + "OUT2": true, + "SensorID": "000008740649", + "Temperature": 22, + "Crest": 3.7, + "v-Rms": 0.0023, + "OUT1": true, + "Device status": 0, + "timestamp": 1685525380729 +} +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.opcua.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.opcua.md new file mode 100644 index 000000000..76a65ca55 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.opcua.md @@ -0,0 +1,76 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.opcua +title: OPC UA +sidebar_label: OPC UA +--- + + + + + +

+ +

+ +*** + +## Description + +Reads values from an OPC-UA server repeatedly + +*** + +## Required Input + +*** + +## Configuration + +### Polling Interval + +Duration of the polling interval in seconds + +### Anonymous vs. Username/Password + +Choose whether you want to connect anonymously or authenticate using your credentials. + +     **Anonymous**: No further information required
+     **Username/Password**: Insert your `username` and `password` to access the OPC UA server + +### OPC UA Server + +Where can the OPC UA server be found? + +     **URL**: Specify the server's full `URL` (including port), can be with our without leading `opc.tcp://`
+     **Host/Port**: Insert the `host` address (with or without leading `opc.tcp://`) and the `port`
+ +### Namespace Index + +Requires the index of the namespace you want to connect to. + +### Node ID + +The identifier of the node you want to read from, numbers and strings are both valid. + +### Available Nodes + +Shows all available nodes once namespace index and node ID are given. +Select as much as you like to query. + +*** diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.modbus.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.modbus.md new file mode 100644 index 000000000..4239ba8b3 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.modbus.md @@ -0,0 +1,75 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.plc4x.modbus +title: PLC4X MODBUS +sidebar_label: PLC4X MODBUS +--- + + + + + +

+ +

+ +*** + +## Description + +The Modbus adapter allows to connect to a PLC using the Modbus specification. + +*** + +## Configuration + +The following configuration options are available when creating the adapter: + +### PLC Address + +The IP address of the Modbus device without any prefix, which will be added automatically when creating the adapter. + +### PLC Port + +The PLC port refers to the port of the PLC, such as 502. + +### Node ID + +The Node ID refers to the ID of the specific device. + +### Nodes + +The `Nodes` section requires configuration options for the individual nodes. +Nodes can be either imported from a comma-separated CSV file, or can be directly assigned in the configuration menu. + +The following fields must be provided for each node: + +* Runtime Name: Refers to the field to internally identify the node, e.g., in the data explorer or pipeline editor. +* Node Address: Refers to the address of the Node in Modbus, e.g., 1 +* Object Type: Can be selected from the available options `DiscreteInput`, `Coil`, `InputRegister`, + or `HoldingRegister`. + +An example CSV file looks as follows: + +``` +Runtime Name,Node Address,Object Type, +field1,1,Coil +temperature,2,Coil +``` + +Note that the CSV header must exactly match the titles `Runtime Name`, `Node Address` and `Object Type`. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.s7.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.s7.md new file mode 100644 index 000000000..9e22be6df --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.s7.md @@ -0,0 +1,96 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.plc4x.s7 +title: PLC4X S7 +sidebar_label: PLC4X S7 +--- + + + + + +

+ +

+ +*** + +## Description + +The adapter allows to connect with a Siemens S7 PLC. + +*** + +## Configuration + +The following configuration options are available when creating an adapter: + +### PLC Address + +This field requires the PLC address in form of the IP without the prefixed protocol (e.g., 192.168.34.56). + +In addition to the pure IP, other parameters supported by Apache PLC4X can be provided as an URL parameter: + +* `local-rack` +* `local-slot` +* `local-tsap` +* `remote-rack` +* `remote-slot` + +Additional configs are separated by `&`. + +Example address: `192.68.34.56?remote-rack=0&remote-slot=3&controller-type=S7_400` + +See the Apache PLC4X documentation for more information. + +### Polling Interval + +The polling interval requires a number in milliseconds, which represents the interval in which the adapter will poll the +PLC for new data. For instance, a polling interval of 1000 milliseconds will configure the adapter to send a request to +the PLC every second. + +### Nodes + +In the Nodes section, the PLC nodes that should be gathered are defined. +There are two options to define the nodes: + +* Manual configuration: The address must be assigned manually by providing a runtime name, the node name and the + datatype. The `Runtime Name` will be the StreamPipes-internal name of the field, which will also show up in the data + explorer and pipeline editor. The `Node Name` refers to the node address of the PLC, e.g., `%Q0.4`. Finally, the data + type can be selected from the available selection. Currently available data types + are `Bool`, `Byte`, `Int`, `Word`, `Real`, `Char`, `String`, `Date`, `Time of Day` and `Date and Time`. +* Instead of providing the node information manually, a CSV file can be uploaded. The CSV file can, for instance, be + exported from TIA and then be enriched with the appropriate runtime names. This is especially useful when many fields + should be added as nodes. Here is an example export enriched with the runtime name: + +``` +Runtime Name,Path,Data Type,Node Name +I_High_sensor,Tag table_1,Bool,%I0.0, +I_Low_sensor,Tag table_1,Bool,%I0.1, +I_Pallet_sensor,Tag table_1,Bool,%I0.2, +I_Loaded,Tag table_1,Bool,%I0.3, +``` + +Note that the CSV can contain additional columns, but only the columns `Runtime Name`, `Data Type` and `Node Name` are +used, while all other columns will be ignored. + +## Best Practices + +Instead of creating a large event containing all nodes that should be available in StreamPipes, consider to group the +fields logically into smaller adapters. +This will ease the definition of pipelines for users and eases future modifications. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.ros.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.ros.md new file mode 100644 index 000000000..aeac39947 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.ros.md @@ -0,0 +1,64 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.ros +title: ROS Bridge +sidebar_label: ROS Bridge +--- + + + + + +

+ +

+ +*** + +## Description + +Connect Robots running on ROS + + +*** + +## Required input + + + +*** + +## Configuration + +Describe the configuration parameters here + +### Ros Bridge + +Example: test-server.com (No protocol) + +### Port + +The port of the ROS instance. + +### Topic + +Example: /battery (Starts with /) + + +## Output + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.simulator.machine.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.simulator.machine.md new file mode 100644 index 000000000..ab03d5adc --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.adapters.simulator.machine.md @@ -0,0 +1,40 @@ +--- +id: org.apache.streampipes.connect.iiot.adapters.simulator.machine +title: Machine Data Simulator +sidebar_label: Machine Data Simulator +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes various simulated machine sensor data in a configurable time interval (in milliseconds). +Sensors are: +* flowrate +* pressure +* waterlevel +*** \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.file.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.file.md new file mode 100644 index 000000000..79efd7ca3 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.file.md @@ -0,0 +1,90 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.file +title: File Stream +sidebar_label: File Stream +--- + + + + + +

+ +

+ +*** + +## Description + +The File Stream Adapter enables continuous streaming of file contents to Apache StreamPipes, creating a data stream for utilization within StreamPipes. It's particularly handy when you prefer not to connect directly to the data source via StreamPipes or for testing and demonstration purposes. Currently, it supports the following file types: + +- CSV +- JSON +- XML + +### Example + +Suppose we have a CSV file (`temperature.csv`) containing data from a temperature sensor recording data every second: + +```text +time,temperature +1715593295000,36.3 +1715593296000,37.5 +1715593297000,37.0 +1715593298000,37.2 +1715593299000,37.2 +1715593210000,37.6 +1715593211000,37.4 +1715593212000,37.5 +1715593213000,37.5 +1715593214000,37.7 +``` + +When creating a new File Stream Adapter: +- Upload the file +- Select `yes` for `Replay Once` +- Choose `CSV` as the `Format` with `,` as the `delimiter`, check `Header` + +After creating the adapter, it will output one line of the CSV as an event every second. +Further details on configuration options are provided below. + +--- + +## Configuration + +### File + +This section determines the file to be streamed by the adapter. Options include: + +- `Choose existing file`: Select from files already present in StreamPipes. +- `Upload new file`: Upload a new file, also available for other adapters. Supports `.csv`, `.json`, and `.xml` file types. + +### Overwrite file time +Enable this option to always pass the current system time as the timestamp when emitting an event. If your file lacks timestamp information, this should be enabled. Conversely, if your file has timestamp information, enabling this option will overwrite it with the current system time. By default, this option is disabled, leaving timestamp information unaffected. + +### Replay Once +Distinguishes between replaying all data contained in the file only once or in a loop until the adapter is manually stopped. +If enabled, this will cause events from the file to be emitted multiple times. In this case, it is recommended to enable `Overwrite file time` if the resulting stream is to be persisted in StreamPipes, otherwise existing events with the same timestamp will be overwritten. + +### Replay Speed + +Configures the event frequency: +- **Keep original time**: Events are emitted based on the timestamp information in the file. +- **Fastest**: All data in the file is replayed as quickly as possible, with no waiting time. +- **Speed Up Factor**: Adjusts the waiting time of the adapter based on the provided speed up factor, considering the time between two events in the file. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.http.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.http.md new file mode 100644 index 000000000..e24df3a09 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.http.md @@ -0,0 +1,38 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.http +title: HTTP Stream +sidebar_label: HTTP Stream +--- + + + + + +

+ +

+ +*** + +## Description + +Continuously fetched events from an HTTP REST endpoint. + +*** + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.httpserver.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.httpserver.md new file mode 100644 index 000000000..366b7ccce --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.httpserver.md @@ -0,0 +1,51 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.httpserver +title: HTTP Server +sidebar_label: HTTP Server +--- + + + + + +

+ +

+ +*** + +## Description + +This adapter provides an HTTP endpoint for ingesting events. +Data sent to this endpoint via POST requests is transformed into StreamPipes events. + +### Configuration + +#### Endpoint Appendix + Specify the name of the endpoint resource. The endpoint can be accessed at {host of StreamPipes UI}/endpoints/{endpointName} + +#### Configuration +##### Manual +Provides an option to define the event schema manually. + +##### Import from file +Use a file with example data to automatically detect a first event schema. + +*** + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.kafka.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.kafka.md new file mode 100644 index 000000000..d2b380f23 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.kafka.md @@ -0,0 +1,38 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.kafka +title: Apache Kafka +sidebar_label: Apache Kafka +--- + + + + + +

+ +

+ +*** + +## Description + +Consumes messages from an Apache Kafka broker + +*** + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.mqtt.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.mqtt.md new file mode 100644 index 000000000..d3f374c07 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.mqtt.md @@ -0,0 +1,53 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.mqtt +title: MQTT +sidebar_label: MQTT +--- + + + + + +

+ +

+ +*** + +## Description + +Consumes messages from a broker using the MQTT protocol + + +*** + +## Configuration + +Describe the configuration parameters here + +### Broker Url + +Example: tcp://test-server.com:1883 (Protocol required. Port required)" + +### Access Mode + +Unauthenticated or Authenticated (Username/Password) + +## Output + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.nats.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.nats.md new file mode 100644 index 000000000..0e43cf54b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.nats.md @@ -0,0 +1,69 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.nats +title: NATS +sidebar_label: NATS +--- + + + + + +

+ +

+ +*** + +## Description + +Consumes events from a NATS broker. + +*** + +## Configuration + +### NATS Subject + +The subject (topic) where events should be received from. When using wildcard subjects, all messages need to have the same format currently. + +### NATS Broker URL + +The URL to connect to the NATS broker. It can be provided multiple urls separated by commas(,). +(e.g., nats://localhost:4222,nats://localhost:4223) + +### Username + +The username to authenticate the client with NATS broker. + +It is an optional configuration. + +### NATS Broker URL + +The password to authenticate the client with NATS broker. + +It is an optional configuration. + +### NATS Connection Properties + +All other possible connection configurations that the nats client can be created with. +It can be provided as key value pairs separated by colons(:) and commas(,). +(e.g., io.nats.client.reconnect.max:1, io.nats.client.timeout:1000) + +It is an optional configuration. + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.pulsar.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.pulsar.md new file mode 100644 index 000000000..f9adc56ce --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.pulsar.md @@ -0,0 +1,38 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.pulsar +title: Apache Pulsar +sidebar_label: Apache Pulsar +--- + + + + + +

+ +

+ +*** + +## Description + +Consumes messages from an Apache Pulsar broker + +*** + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.rocketmq.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.rocketmq.md new file mode 100644 index 000000000..d6c5cb32d --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.rocketmq.md @@ -0,0 +1,38 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.rocketmq +title: Apache RocketMQ +sidebar_label: Apache RocketMQ +--- + + + + + +

+ +

+ +*** + +## Description + +Consumes messages from an Apache RocketMQ broker + +*** + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.tubemq.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.tubemq.md new file mode 100644 index 000000000..0f321339c --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.connect.iiot.protocol.stream.tubemq.md @@ -0,0 +1,54 @@ +--- +id: org.apache.streampipes.connect.iiot.protocol.stream.tubemq +title: Apache TubeMQ (InLong) +sidebar_label: Apache TubeMQ (InLong) +--- + + + + + +

+ +

+ +*** + +## Description + +Consumes messages from an Apache TubeMQ broker. + +*** + +## Configuration + +### TubeMQ Master Information + +This field describes the endpoints of all the TubeMQ masters. + +The format should be like `ip1:port1,ip2:port2,ip3:port3`. + +### TubeMQ Topic + +The topic where events should be sent to. + +### TubeMQ Consumer Group + +The consumer group of the TubeMQ Consumer. + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification.md new file mode 100644 index 000000000..fb42a1a00 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification.md @@ -0,0 +1,52 @@ +--- +id: org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification +title: Generic Image Classification +sidebar_label: Generic Image Classification +--- + + + + +

+ +

+ +*** + +## Description + +Image + Classification Description (Generic Model) + +*** + +## Required input + +Input events require to have an image field. + +*** + +## Configuration + +Describe the configuration parameters here + +### Image field + +Field that contains the image. + +## Output \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-cropper.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-cropper.md new file mode 100644 index 000000000..e2cd6b0e6 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-cropper.md @@ -0,0 +1,43 @@ +--- +id: org.apache.streampipes.processor.imageclassification.jvm.image-cropper +title: Image Cropper +sidebar_label: Image Cropper +--- + + + + +

+ +

+ +*** + +## Description + +Image Enrichment: Crops an + image based on + given bounding box coordinates + +*** + +## Required input +An image and an array with bounding boxes. +A box consists of the x and y coordinates in the image as well as the height and width + +## Output +A new event for each box containing the cropped image \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-enricher.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-enricher.md new file mode 100644 index 000000000..8a09f3ae8 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.jvm.image-enricher.md @@ -0,0 +1,43 @@ +--- +id: org.apache.streampipes.processor.imageclassification.jvm.image-enricher +title: Image Enricher +sidebar_label: Image Enricher +--- + + + + + +

+ +

+ +*** + +## Description + +Image Enrichment: Enriches an + image with + given bounding box coordinates + +## Required input + +An image and an array with bounding boxes, an array with scores and an array with labels. +A box consists of the x and y coordinates in the image as well as the height and width, and the classindex with score + +## Output +A new event containing the image with bounding boxes rendered according to the boxes of the input event \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.qrcode.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.qrcode.md new file mode 100644 index 000000000..74f12344f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processor.imageclassification.qrcode.md @@ -0,0 +1,68 @@ +--- +id: org.apache.streampipes.processor.imageclassification.qrcode +title: QR Code Reader +sidebar_label: QR Code Reader +--- + + + + + +

+ +

+ +*** + +## Description + +QR Code Reader: Detects a QR Code in an image + +*** + +## Required input + +Input events require to have an image field. + +*** + +## Configuration + +### Image + +Image of the QR code + +### Send placeholder value if no qr code is detected + +It is a boolean selection. + +### Placeholder value + +Place holder value + +## Output + +Outputs a similar event like below. + +``` +{ + 'qrvalue': 'http://githhub.com/', + 'timestamp': 1621244783151 +} +``` \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.changedetection.jvm.welford.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.changedetection.jvm.welford.md new file mode 100644 index 000000000..10f95361f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.changedetection.jvm.welford.md @@ -0,0 +1,72 @@ +--- +id: org.apache.streampipes.processors.changedetection.jvm.welford +title: Welford Change Detection +sidebar_label: Welford Change Detection +--- + + + + + + + +*** + +## Description + +Performs change detection on a single dimension of the incoming data stream. This implementation tracks the mean and the +standard deviation using Welford's algorithm, which is well suited for data streams. A change is detected if the +cumulative deviation from the mean exceeds a certain threshold. + +*** + +## Required input + +The welford change dectection processor requires a data stream that has at least one field containing a numerical value. + +*** + +## Configuration + +### Value to observe + +Specify the dimension of the data stream (e.g. the temperature) on which to perform change detection. + +### Parameter `k` + +`k` controls the sensitivity of the change detector. Its unit are standard deviations. For an observation `x_n`, the +Cusum value is `S_n = max(0, S_{n-1} - z-score(x_n) - k)`. Thus, the cusum-score `S` icnreases +if `S_{n-1} - z-score(x_n) > k`. + +### Parameter `h` + +The alarm theshold in standard deviations. An alarm occurs if `S_n > h` + +## Output + +This processor outputs the original data stream plus + +- `cumSumLow`: The cumulative sum value for negative changes +- `cumSumHigh`: The cumulative sum value for positive changes +- `changeDetectedLow`: Boolean indicating if a negative change was detected +- `changeDetectedHigh`: Boolean indicating if a positive change was detected \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.jseval.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.jseval.md new file mode 100644 index 000000000..3fe018306 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.jseval.md @@ -0,0 +1,55 @@ +--- +id: org.apache.streampipes.processors.enricher.jvm.jseval +title: JavaScript Eval +sidebar_label: JavaScript Eval +--- + + + + + +

+ +

+ +*** + +## Description +A pipeline element that allows writing user defined JavaScript function to enrich events. + +*** + +## Required input +This processor does not have any specific input requirements. + +*** + +## Configuration +User can specify their custom enrichment logic within the `process` method. Please note that the `process` function +must be in the following format and it must return a map of data which is compatible with the output schema. +```javascript + function process(event) { + // do processing here. + // return a map with fields that matched defined output schema. + return {id: event.id, tempInCelsius: (event.tempInKelvin - 273.15)}; + } +``` + +## Output +A new event with the user defined output schema. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.mathop.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.mathop.md new file mode 100644 index 000000000..859708971 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.mathop.md @@ -0,0 +1,56 @@ +--- +id: org.apache.streampipes.processors.enricher.jvm.processor.math.mathop +title: Math +sidebar_label: Math +--- + + + + + +

+ +

+ +*** + +## Description + +Performs calculations on event properties (+, -, *, /, %). + +*** + +## Required input +The math processor works with any event that has at least one field containing a numerical value. + +*** + +## Configuration + +### Left operand +The field from the input event that should be used as the left operand. + +### Right operand +The field from the input event that should be used as the right operand. + +### Operation +The math operation that should be performed. + +## Output +The processor appends the calculation result to each input event. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.staticmathop.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.staticmathop.md new file mode 100644 index 000000000..ce274a8ea --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.math.staticmathop.md @@ -0,0 +1,56 @@ +--- +id: org.apache.streampipes.processors.enricher.jvm.processor.math.staticmathop +title: Static Math +sidebar_label: Static Math +--- + + + + + +

+ +

+ +*** + +## Description + +Performs calculation on an event property with a static value (+, -, *, /, %). + +*** + +## Required input +The math processor works with any event that has at least one field containing a numerical value. + +*** + +## Configuration + +### Left operand +The field from the input event that should be used as the left operand. + +### Right operand value +Specify the value of the right operand. + +### Operation +The math operation that should be performed. + +## Output +The processor appends the calculation result to each input event. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.trigonometry.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.trigonometry.md new file mode 100644 index 000000000..f5d55cca4 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.processor.trigonometry.md @@ -0,0 +1,56 @@ +--- +id: org.apache.streampipes.processors.enricher.jvm.processor.trigonometry +title: Trigonometry Functions +sidebar_label: Trigonometry Functions +--- + + + + + +

+ +

+ +*** + +## Description + +Performs Trigonometric functions (sin, cos, tan) on event properties. + +*** + +## Required input +The trigonometry processor works with any event that has at least one field containing a numerical value. + +*** + +## Configuration + +Describe the configuration parameters here + +### Alpha +The field that should be used for calculating the trigonometric function. + + +### Operation +The trigonometric function that should be calculated. + +## Output +The processor appends the calculation result to each input event. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.valuechange.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.valuechange.md new file mode 100644 index 000000000..92718d815 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.enricher.jvm.valuechange.md @@ -0,0 +1,52 @@ +--- +id: org.apache.streampipes.processors.enricher.jvm.valuechange +title: Value Change +sidebar_label: Value Change +--- + + + + + +

+ +

+ +*** + +## Description + +The processing element should be able to detect when a numeric property change from one configured value to another. + +*** + +## Required input +The required input is a number. + +*** + +## Configuration +Value of last event (example: 0) + +Value of current event (example: 5) + + +## Output +A boolean value is returned when the input changes. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.compose.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.compose.md new file mode 100644 index 000000000..8cb669890 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.compose.md @@ -0,0 +1,50 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.compose +title: Compose +sidebar_label: Compose +--- + + + + + +

+ +

+ +*** + +## Description + +Merges two event streams. Any time, a new input event arrives, it is merged with the last input event from the other +event stream and forwarded. + +*** + +## Required input +The Compose processor does not have any specific input requirements. + +*** + +## Configuration + +(no further configuration required) + +## Output +The compose processor has a configurable output that can be selected by the user at pipeline modeling time. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.enrich.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.enrich.md new file mode 100644 index 000000000..5dfb0d96b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.enrich.md @@ -0,0 +1,47 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.enrich +title: Merge Two Streams +sidebar_label: Merge Two Streams +--- + + + + + +

+ +

+ +*** + +## Description +Merges two data streams by enriching one of the streams with the properties of the other stream. The output frequency is the same as the frequency of the stream which is enriched. +*** + +## Required input +None +*** + +## Configuration + +* Select the stream which should be enriched with the properties of the other stream. + * The last event of the stream is hold in state and each event of the other stream is enriched by the properties the user selected + +## Output +The compose processor has a configurable output that can be selected by the user at pipeline modeling time. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.limit.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.limit.md new file mode 100644 index 000000000..087d72ff1 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.limit.md @@ -0,0 +1,70 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.limit +title: Rate Limit +sidebar_label: Rate Limit +--- + + + + + +

+ +

+ +*** + +## Description +This limits the number of events emitted based on a specified criterion such as time, and number of events. + +*** + +## Required input +The processor works with any input event. + +*** + +## Configuration + +### Enable Grouping +Enabling this will use grouping with rate-limiting (note: disabling this will ignore `Grouping Field` property). + +### Grouping Field +Runtime field to be used as the grouping key. If grouping is disabled, this setting will be ignored. + +### Window Type +This specifies the type of window to be used (time / length / cron). + +### Length Window Size +Length window size in event count (note: only works with length window type). + +### Time Window Size +Time window size in milliseconds (note: only works with time window type). + +### Cron Window Expression +Cron expression [Link](https://www.freeformatter.com/cron-expression-generator-quartz.html) to trigger and emit events (i.e `0 * * ? * *` for every minute) (note: only works with cron window type). + +### Output Event Selection +This specifies the event(s) that are selected to be emitted. +- First: emit first event of the window. +- Last: emit last event of the window. +- All: emit all events of the window. + +## Output +The processor outputs events which satisfies rate-limiting conditions. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.merge.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.merge.md new file mode 100644 index 000000000..569b5ee2f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.merge.md @@ -0,0 +1,57 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.merge +title: Synchronize Two Streams +sidebar_label: Synchronize Two Streams +--- + + + + + +

+ +

+ +*** + +## Description + +Merges two event streams by their timestamp. +Two events of the different streams are merged when they occure to the same time + +The following figure shows how the events of the two data streams will be mergrged: + +

+ +

+ +*** + +## Required input +Each of the data streams needs a timestamp. + +*** + +## Configuration + +* For each stream a the timestamp property on which the merger is performed has to be selected +* The Time Interval describes the maximum value between two events to decide whether they are a match. To be a valid match the following function must be true: | timestamp_stream_1 - timestamp_stream_2 | < interval + +## Output +The Compose processor has a configurable output that can be selected by the user at pipeline modeling time. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.movingaverage.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.movingaverage.md new file mode 100644 index 000000000..2b6cad6d5 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.movingaverage.md @@ -0,0 +1,46 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.movingaverage +title: Moving Average +sidebar_label: Moving Average +--- + + + + +*** + + +## Description + +Smooths the data stream by the mean/median of the last n values. + +*** + +## Required input +A numerical field is required. +*** + +## Configuration +### N Value +Specifies the number of previous data points which are used to smooth the data. +### Method +Specifies the method which is used to smooth the data. Choose between mean and median. + +## Output +Appends a field with the smoothed data. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericalfilter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericalfilter.md new file mode 100644 index 000000000..55c320801 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericalfilter.md @@ -0,0 +1,56 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.numericalfilter +title: Numerical Filter +sidebar_label: Numerical Filter +--- + + + + + +

+ +

+ +*** + +## Description +The Numerical Filter processor filters numerical values based on a given threshold. + +*** + +## Required input +The processor works with any input event that has one field containing a numerical value. + +*** + +## Configuration + +### Field +Specifies the field name where the filter operation should be applied on. + + +### Operation +Specifies the filter operation that should be applied on the field. + +### Threshold value +Specifies the threshold value. + +## Output +The processor outputs the input event if it satisfies the filter expression. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericaltextfilter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericaltextfilter.md new file mode 100644 index 000000000..90466f092 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.numericaltextfilter.md @@ -0,0 +1,67 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.numericaltextfilter +title: Numerical Text Filter +sidebar_label: Numerical Text Filter +--- + + + + + +

+ +

+ + +*** + +## Description +The Numerical Text Filter processor filters numerical values based on a given threshold and text values +based on a given string. It only forwards events in case both criteria are satisfied. + +*** + +## Required input +The processor works with any input event that has one field containing a numerical value and one field +containing a text. + +*** + +## Configuration + +### Number Field +Specifies the field name where the filter operation should be applied on. + +### Number Operation +Specifies the filter operation that should be applied on the field. + +### Number Threshold +Specifies the threshold value. + +### Text Field +The field containing the text that should be filtered. + +### Text Operation +The operation used by the filter processor (equals or matches). + +### Text Keyword +Specifies the keyword to filter the text field. + +## Output +The processor outputs the input event if it satisfies the filter expression. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.processor.booleanfilter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.processor.booleanfilter.md new file mode 100644 index 000000000..1f2d71468 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.processor.booleanfilter.md @@ -0,0 +1,52 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.processor.booleanfilter +title: Boolean Filter +sidebar_label: Boolean Filter +--- + + + + + +

+ +

+ +*** + +## Description +The Boolean Filter processor filters based on a boolean value field + +*** + +## Required Input +The processor works with any input event that has one field containing a boolean value + +*** + +## Configuration + +### Field +Specifies the field name where the filter operation should be applied on. + +### Field Value +Events with the selected field value are forwarded + +## Output +The processor outputs the input event if the event value is equals the selected field value \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.project.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.project.md new file mode 100644 index 000000000..69a0616bb --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.project.md @@ -0,0 +1,48 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.project +title: Projection +sidebar_label: Projection +--- + + + + + +

+ +

+ +*** + +## Description +Outputs a selectable subset of an input event type. + +*** + +## Required input +The project processor works with any input event stream. + +*** + +## Configuration + +(no further configuration required) + +## Output +The output depends on the fields selected at pipeline development time. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.schema.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.schema.md new file mode 100644 index 000000000..ebdfc66e0 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.schema.md @@ -0,0 +1,46 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.schema +title: Merge stream with same schema +sidebar_label: Merge stream with same schema +--- + + + + + + + +## Description + +Merges two events by their schema. +It checks two whether the schemas of two events are equal or not. +If the schemas are not equal we can throw SpRuntimeException otherwise we can collect them. + +*** + +## Required input +Two events are needed. +*** + +## Configuration + +For each stream schema info should be present + +## Output +Events with same schemas. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.sdt.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.sdt.md new file mode 100644 index 000000000..02de52173 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.sdt.md @@ -0,0 +1,85 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.sdt +title: Swinging Door Trending (SDT) Filter Processor +sidebar_label: Swinging Door Trending (SDT) Filter Processor +--- + + + + + +

+ +

+ + +*** + +## Description + +The **Swinging Door Trending (SDT)** algorithm is a linear trend compression algorithm. +In essence, it replaces a series of continuous `(timestamp, value)` points with a straight line determined by the start and end points. + +The **Swinging Door Trending (SDT) Filter Processor** can extract and forward the characteristic events of the original stream. +In general, this filter can also be used to reduce the frequency of original data in a lossy way. + +*** + +## Required Inputs + +The processor works with any input event that has **one field containing a timestamp** and +**one field containing a numerical value**. + +*** + +## Configuration + +### Timestamp Field +Specifies the timestamp field name where the SDT algorithm should be applied on. + +### Value Field +Specifies the value field name where the SDT algorithm should be applied on. + +### Compression Deviation +**Compression Deviation** is the most important parameter in SDT that represents the maximum difference +between the current sample and the current linear trend. + +**Compression Deviation** needs to be greater than 0 to perform compression. + +### Compression Minimum Time Interval +**Compression Minimum Time Interval** is a parameter measures the time distance between two stored data points, +which is used for noisy reduction. + +If the time interval between the current point and the last stored point is less than or equal to its value, +current point will NOT be stored regardless of compression deviation. + +The default value is `0` with time unit ms. + +### Compression Maximum Time Interval +**Compression Maximum Time Interval** is a parameter measure the time distance between two stored data points. + +If the time interval between the current point and the last stored point is greater than or equal to its value, +current point will be stored regardless of compression deviation. + +The default value is `9,223,372,036,854,775,807`(`Long.MAX_VALUE`) with time unit ms. + +*** + +## Output +The characteristic event stream forwarded by the SDT filter. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.textfilter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.textfilter.md new file mode 100644 index 000000000..ce5c254b6 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.textfilter.md @@ -0,0 +1,53 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.textfilter +title: Text Filter +sidebar_label: Text Filter +--- + + + + + +

+ +

+ +*** + +## Description +The Text Filter processor filters text values based on a given string. + +*** + +## Required input +The processor works with any input event that has one field containing a text. + +*** + +## Configuration + +### Text Field +The field containing the text that should be filtered. + + +### Operation +The operation used by the filter processor (equals or matches) + +## Output +The processor outputs the input event if it satisfies the filter expression. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.threshold.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.threshold.md new file mode 100644 index 000000000..6ffa67058 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.threshold.md @@ -0,0 +1,56 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.threshold +title: Threshold Detector +sidebar_label: Threshold Detector +--- + + + + + +

+ +

+ +*** + +## Description +The Threshold Detector processor appends a boolean whether the condition is fulfilled or not + +*** + +## Required input +The processor works with any input event that has one field containing a numerical value. + +*** + +## Configuration + +### Field +Specifies the field name where the filter operation should be applied on. + + +### Operation +Specifies the filter operation that should be applied on the field. + +### Threshold value +Specifies the threshold value. + +## Output +Appends a boolean with the value whether the condition is fulfilled or not. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.throughputmon.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.throughputmon.md new file mode 100644 index 000000000..f97b5f24b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.filters.jvm.throughputmon.md @@ -0,0 +1,56 @@ +--- +id: org.apache.streampipes.processors.filters.jvm.throughputmon +title: Throughput Monitor +sidebar_label: Throughput Monitor +--- + + + + + +

+ +

+ +*** + +## Description +The Throughput Monitoring processor computes throughput statistics. + +*** + +## Required Input +The processor works with any input event. + +*** + +## Configuration + +### Batch Window Size +Specifies the number of events that should be used for calculating throughput statistics. + + +## Output +The processor outputs a new event containing: +* The current timestamp (timestamp) +* The start time of the batch window (starttime) +* The end time of the batch window (endtime) +* The duration between both windows (duration) +* The number of events collected in the window (should be equal to batch size) +* The throughput in events per second diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.buffergeometry.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.buffergeometry.md new file mode 100644 index 000000000..a022374e7 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.buffergeometry.md @@ -0,0 +1,95 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.buffergeometry +title: Geo Buffer Geometry +sidebar_label: Geo Buffer Geometry +--- + + + + + +

+ +

+ +*** + +## Description + +Creates a buffer polygon geometry from a geometry +*** + +## Required inputs + +* JTS Geometry +* EPSG Code +* Distance +* Cap Style +* Join Style +* Mitre-Limit +* Side +* Simplify Factor +* Quadrant Segments +*** + +## Configuration + +### Geometry field +Input Geometry + +### EPSG field +Integer value representing EPSG code + +### Distance +The buffer distance around in geometry in meter + +### Cap Style +Defines the endcap style of the buffer. +* CAP_ROUND - the usual round end caps +* CAP_FLAT - end caps are truncated flat at the line ends +* CAP_SQUARE - end caps are squared off at the buffer distance beyond the line ends + +### Simplify Factor +The default simplify factor Provides an accuracy of about 1%, which matches the accuracy of the +default Quadrant Segments parameter. + +### Quadrant Segments +The default number of facets into which to divide a fillet of 90 degrees. + +### Join Style +Defines the corners in a buffer +* JOIN_ROUND - the usual round join +* JOIN_MITRE - corners are "sharp" (up to a distance limit) +* JOIN_BEVEL - corners are beveled (clipped off). + +### Mitre-Limit +Mitre ratio limit (only affects mitered join style) + +### Side +`left` or `right` performs a single-sided buffer on the geometry, with the buffered side +relative to the direction of the line or polygon. + +*** + +## Output +A polygon geometry with EPSG code. Shape is defined by input parameters. + + +### Example + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.bufferpoint.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.bufferpoint.md new file mode 100644 index 000000000..04346ce85 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.bufferpoint.md @@ -0,0 +1,82 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.bufferpoint +title: Geo Buffer Point +sidebar_label: Geo Buffer Point +--- + + + + + +

+ +

+ +*** + +## Description + +Creates a buffer polygon geometry from a point geometry +*** + +## Required inputs + +* JTS Geometry +* EPSG Code +* Distance +* Cap Style +* Simplify Factor +* Quadrant Segments +*** + +## Configuration + +### Geometry Field +Input Point Geometry + +### EPSG field +Integer value representing EPSG code + +### Distance +The buffer distance around the geometry in meter + +### Cap Style +Defines the endcap style of the buffer. +CAP_ROUND - the usual round end caps +CAP_SQUARE - end caps are squared off at the buffer distance beyond the line ends + + +### Simplify Factor +The default simplify factor provides an accuracy of about 1%, which matches the accuracy of the +default Quadrant Segments parameter. + +### Quadrant Segments +The default number of facets into which to divide a fillet of 90 degrees. + +*** + +## Output +A polygon geometry with EPSG code. Shape is defined by input parameters. + +

+ +

+ +### Example + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.epsg.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.epsg.md new file mode 100644 index 000000000..294124c81 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.epsg.md @@ -0,0 +1,64 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.epsg +title: Geo EPSG Code +sidebar_label: Geo EPSG Code +--- + + + + + +

+ +

+ + +*** + +## Description + +This processor adds an integer value to the event. This integer value represents +an EPSG Code as an Spatial Reference System Identifier +an (SRID). + + +*** + +## Required inputs + +None + +*** + +## Configuration + +Integer values, representing a spatial reference system +SRID. +Other possible values can be looked up via +spatialreference.org. + +### Parameter + +4- to 5-digit key integer number. Default value is 4326 representing the World Geodetic System +(WGS84). + +*** +## Output + +Adds the epsg number to the event. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.latlngtojtspoint.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.latlngtojtspoint.md new file mode 100644 index 000000000..acc01928f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.latlngtojtspoint.md @@ -0,0 +1,73 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.latlngtojtspoint +title: Geo Create JTS Point +sidebar_label: Geo Create JTS Point +--- + + + + + +

+ +

+ +*** + +## Description + +This processor creates a JTS Point geometry from latitude and longitude value. + +*** + +## Required inputs + +* Ontology Vocabulary Latitude +* Ontology Vocabulary Longitude +* Integer value representing EPSG Code + + +*** + +## Configuration + +Creates a JTS Geometry Point from Longitude (x) and Latitude (y) values in the coordinate reference system represented by the EPSG code. +An empty point geometry is created if latitude or longitude value is missing in the event (e.g. null value) or values are out of range. Allowed values for Longitude are between -180.00 and 180.00; Latitude values between -90.00 and 90.00. + +### 1st parameter +Latitude value + +### 2nd parameter +Longitude value + +### 3rd parameter +EPSG code value + +*** + +## Output + +Adds a point geometry in the Well Known Text notation and in Longitude (x) Latitude (y) axis order to the stream. + +### Example +* Input stream:
+ `{latitude=48.5622, longitude=-76.3501, EPSG=4326}` + +* Output Stream
+ `{latitude=48.5622, longitude=-76.3501, EPSG=4326, geom_wkt=POINT (-76.3501 48.5622)}` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.reprojection.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.reprojection.md new file mode 100644 index 000000000..99ea9b9c1 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.reprojection.md @@ -0,0 +1,68 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.reprojection +title: Geo CRS Reprojection +sidebar_label: Geo CRS Reprojection +--- + + + + + +

+ +

+ +*** + +## Description + +Change of CRS due reprojection + +*** + +## Required input + +* WKT String of a JTS Point Geometry +* Integer value representing Source EPSG code +* Integer value representing Target EPSG code + + +*** + +## Configuration + +Manual Input of target EPSG Code and WKT will be reporjected. + +### 1st parameter +Geometry WKT String + +### 2nd parameter +Source EPSG code + +### 3rd parameter +Target EPSG code + +*** + +## Output + +Update of event with new EPSG udn WKT-Literal in the dependent target epsg. + +### Example + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.trajectory.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.trajectory.md new file mode 100644 index 000000000..ee602fee6 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.trajectory.md @@ -0,0 +1,83 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.trajectory +title: Geo Single Trajectory Creator +sidebar_label: Geo Single Trajectory Creator +--- + + + + + +

+ +

+ +*** + +## Description + +This processor creates a JTS LineString geometry from JTS Points events, represent a trajectory. A trajectory is defined as the path that a moving object follows through space as a function of time. Each sub-point of this LineString represents a single event. The latest sub-point represents the latest geo-event. For each Point event it is also possible to store an additional m-value representing for example actually speed, distance, duration or direction of this event. A trajectory consists of at least two sub-point and can't be infinitive, so a threshold of maximum allowed sub-points is required. When the sub-point threshold is exceeded, the oldest point is removed from the LineString. +*** + +## Required inputs + +* WKT String of a JTS Point Geometry +* Integer value representing EPSG code +* Number value for M-value + + +*** + +## Configuration + +Creates a JTS Geometry LineString from a JTS Point Geometries events representing a trajectory. + + +### 1st parameter +Point WKT String + +### 2nd parameter +EPSG code value + +### 3rd parameter +M-value for each sub-point of the trajectory + +### 4rd parameter +String for a description text for the trajectory + +### 5rd parameter +Number of allowed sub-points + +*** + +## Output + +Adds a LineString geometry in the Well Known Text to the event, representing a trajectory. Also the description text is added to the event stream. The first existing event creates an empty LineString. + +### Example +Creating a LineString with a threshold of 2 allowed sub-points: + +* First Event: + * Point(8.12 41.23) --> LineString(empty) +* Second Event: + * Point(8.56 41.25) --> LineString(8.12 41.23, 8.56 41.25) +* Second Event: + * Point(8.84 40.98) --> LineString(8.56 41.25, 8.84 40.98) + +M-value is not represented in the LineString but will be stored for internal use! diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.complex.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.complex.md new file mode 100644 index 000000000..9cd2db4b2 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.complex.md @@ -0,0 +1,86 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.validation.complex +title: Geo Geometry Topology Validation Filter +sidebar_label: Geo Geometry Topology Validation Filter +--- + + + + + +

+ +

+ +*** + +## Description +Validates geometry of topology +erros from JTS. + +* **HOLE_OUTSIDE_SHELL**: Indicates that a hole of a polygon lies partially or completely in the exterior of the shell +* **NESTED_HOLES**: Indicates that a hole lies in the interior of another hole in the same polygon +* **DISCONNECTED_INTERIOR**: Indicates that the interior of a polygon is disjoint (often caused by set of contiguous holes splitting the polygon into two parts) +* **SELF_INTERSECTION**: Indicates that two rings of a polygonal geometry intersect +* **RING_SELF_INTERSECTION**: Indicates that a ring self-intersects +* **NESTED_SHELLS**: Indicates that a polygon component of a MultiPolygon lies inside another polygonal component +* **DUPLICATE_RINGS**: Indicates that a polygonal geometry contains two rings which are identical +* **TOO_FEW_POINTS**: Indicates that either a LineString contains a single point or a LinearRing contains 2 or 3 points +* **RING_NOT_CLOSED**: Indicates that a ring is not correctly closed (the first and the last coordinate are different) + + +*** + +## Required inputs + +* JTS Geometry +* EPSG Code +* Validation Type +* Log Output Option + + +*** + +## Configuration + +### Point Geometry Field +Input Point Geometry + +### EPSG field +Integer value representing EPSG code + +### Validation Output +Chose the output result of the filter. +* Valid - all valid events are parsed through +* Invalid - all invalid events are parsed through + + +### Log Output Option +Options to activate Log-Output to the Pipeline Logger Window with detailed reason why Geometry is invalid + + +*** + +### Default Validation Checks + +## Output + +All events that match the validation output. + +### Example diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.simple.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.simple.md new file mode 100644 index 000000000..21c77b1b1 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.simple.md @@ -0,0 +1,80 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.jts.processor.validation.simple +title: Geo Geometry Validation Filter +sidebar_label: Geo Geometry Validation Filter +--- + + + + + +

+ +

+ +*** + +## Description + +Checks the geometry event if the geometry is simple and / or empty. + +*** + +## Required inputs + +* JTS Geometry +* EPSG Code +* Validation Type +* Validation Output + + +*** + +## Configuration + +Validates geometry of different validations categories. + + +### Point Geometry Field +Input Point Geometry + +### EPSG field +Integer value representing EPSG code + +### Validation Type +* IsEmpty - Geometry is empty. +* IsSimple - Geometry is simple. The SFS definition of simplicity follows the general rule that a Geometry is simple if it has no points of self-tangency, self-intersection or other anomalous points. + * Valid polygon geometries are simple, since their rings must not self-intersect. + * Linear rings have the same semantics. + * Linear geometries are simple if they do not self-intersect at points other than boundary points. + * Zero-dimensional geometries (points) are simple if they have no repeated points. + * Empty Geometries are always simple! + +### Validation Output +Chose the output result of the filter. +* Valid - all valid events are parsed through +* Invalid - all invalid events are parsed through + +*** + +## Output + +All events that match the validation output. + +### Example diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversine.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversine.md new file mode 100644 index 000000000..075dd23d0 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversine.md @@ -0,0 +1,61 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversine +title: Geo Distance Calculator (Haversine) +sidebar_label: Geo Distance Calculator (Haversine) +--- + + + + + +

+ +

+ +*** + +## Description +Calculates the distance between two latitude/longitude pairs in a single event with the +Haversine formula. + +*** + +## Required inputs +Requires a position of point on the Earth's surface specified by the two geographic coordinates: the longitude and latitude of the point. + +*** + +## Configuration + +### First Longitude +This is the first geographic coordinate that specifies the east-west position of a point on the Earth's surface. + +### First Latitude +This is the second geographic coordinate that specifies the north-south position of a point on the Earth's surface. + +### Second Longitude +This is the second geographic coordinate that specifies the east-west position of a point on the Earth's surface. + +### Second Latitude +This is the second geographic coordinate that specifies the north-south position of a point on the Earth's surface. + +## Output +{ + 'distance': 12.2 +} \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversinestatic.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversinestatic.md new file mode 100644 index 000000000..0cd8b8c33 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversinestatic.md @@ -0,0 +1,74 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversinestatic +title: Geo Distance Calculator Static (Haversine) +sidebar_label: Geo Distance Calculator Static (Haversine) +--- + + + + + +

+ +

+ +*** + +## Description + +Calculates the distance with the Haversine formula between a fixed location (e.g., a place) and a latitude/longitude pair of an input + event. + +*** + +## Required inputs + +Requires a data stream that provides latitude and longitude values. + +*** + +## Configuration + +Describe the configuration parameters here + +### Latitude field + +The field containing the latitude value. + +### Longitude field + +The field containing the longitude value. + +### Latitude + +The latitude value of the fixed location + +### Longitude + +The longitude value of the fixed location + +## Output + +Outputs a similar event like below. + +``` +{ + 'distance': 12.5 +} +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemaps.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemaps.md new file mode 100644 index 000000000..ce310f573 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemaps.md @@ -0,0 +1,61 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemaps +title: Geo Google Maps Geocoder +sidebar_label: Geo Google Maps Geocoder +--- + + + + + +

+ +

+ +*** + +## Description + +This processor computes the latitude and longitude values from a location (a place name such as "Karlsruhe, Germany +") and adds the result to the event. + +*** + +## Required inputs + +Input event requires to have a field which contains the name of a place. + +*** + +## Configuration + +### Place + +The field of the input event that should be used to compute the lat/lng values. + +## Output + +Outputs a similar event like below. + +``` +{ + 'latitude': 6.927079, + 'longitude': 79.861244 +} +``` \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemapsstatic.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemapsstatic.md new file mode 100644 index 000000000..6f5a443df --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemapsstatic.md @@ -0,0 +1,62 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemapsstatic +title: Geo Google Maps Static Geocoder +sidebar_label: Geo Google Maps Static Geocoder +--- + + + + + +

+ +

+ +*** + +## Description + +This processor computes the latitude and longitude values from a fixed location (a place name such as "Karlsruhe +, Germany +") and adds the result to the event. + +*** + +## Required inputs + +Input event requires to have a field which contains the name of a place. + +*** + +## Configuration + +### Place + +The place name that should be converted to a lat/lng combination + +## Output + +Outputs a similar event like below. + +``` +{ + 'latitude': 6.927079, + 'longitude': 79.861244 +} +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.revgeocoder.geocityname.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.revgeocoder.geocityname.md new file mode 100644 index 000000000..8375bb7e9 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.revgeocoder.geocityname.md @@ -0,0 +1,67 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.latlong.processor.revgeocoder.geocityname +title: Geo City Name Reverse Decoder +sidebar_label: Geo City Name Reverse Decoder +--- + + + + + +

+ +

+ +*** + +## Description + +This processor computes city name based on given lat/lng coordinates that are transmitted as fields from an event. +This processor automatically downloads the file cities1000.zip from Geonames + ( This file is provided under the CC BY 4.0 license). + + + +*** + +## Required inputs + +Input event requires to have latitude and longitude values. + +*** + +## Configuration + +### Latitude + +The field containing the latitude value. + +### Longitude + +The field containing the longitude value. + +## Output + +Outputs a similar event like below. + +``` +{ + 'geoname': 'Colombo' +} +``` \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.speedcalculator.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.speedcalculator.md new file mode 100644 index 000000000..543f26cdc --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.speedcalculator.md @@ -0,0 +1,59 @@ +--- +id: org.apache.streampipes.processors.geo.jvm.latlong.processor.speedcalculator +title: Geo Speed Calculator +sidebar_label: Geo Speed Calculator +--- + + + + + +

+ +

+ +*** + +## Description + +Calculates the speed (in km/h) based on latitude/longitude values in a data stream. Therefore, it uses the GPS and timestamps values of consecutive events. +It calculates the distance between two points (events) and how much time has passed. Based on those values the speed is calculated. + +*** + +## Required inputs + +Requires a data stream that provides latitude and longitude values as well as a timestamp. + +*** + +## Configuration + +### Timestamp field + +### Latitude field + +### Longitude field + +### Count window +Describes the number of stored events, used for the calculation. +E.g. a value of 5 means that the current event and the event (t-5) are used for the speed calculation. + +## Output +Appends the calculated speed in km/h. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.count.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.count.md new file mode 100644 index 000000000..c2f593fbb --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.count.md @@ -0,0 +1,66 @@ +--- +id: org.apache.streampipes.processors.siddhi.count +title: Count Value Occurrence +sidebar_label: Count Value Occurrence +--- + + + + + +Performs count aggregation with Siddhi CEP engine. + +*** + +## Description + +Performs an aggregation based on a given field and outputs the number of occurrences. +Example: Count the number of vehicle positions per vehicleId. +The Count aggregation requires a time window, used to perform the count aggregation and a field used to aggregate +values. + +*** + +## Required input + +Does not have any specific input requirements. + +*** + +## Configuration + +### FieldToCount +Specifies the field containing the values that should be counted. + +### TimeWindowSize +Specifies the size of the time window and consequently the number of values that are aggregated each time. + +### Time Window Scale +Specifies the scale/unit of the time window. There are three different time scales to choose from: seconds, minutes or hours. + +## Output +The output event is composed of two fields. The field "value" specifies the value to count. +The second field "count" returns the number of occurrences. +Example: +``` +{ + 'value': 'vehicleId', + 'count': 12 +} +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.increase.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.increase.md new file mode 100644 index 000000000..200a7f8da --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.increase.md @@ -0,0 +1,65 @@ +--- +id: org.apache.streampipes.processors.siddhi.increase +title: Trend +sidebar_label: Trend +--- + + + + + +

+ +

+ +*** + +## Description + +Detects the increase of a numerical field over a customizable time window. Example: A temperature value increases by 10 percent within 5 minutes. + +*** + +## Required input + +There should be a number field in the event to observe the trend. + +*** + +## Configuration + +### Value to Observe + +Specifies the value field that should be monitored. + +### Increase/Decrease + +Specifies the type of operation the processor should perform. + +### Percentage of Increase/Decrease + +Specifies the increase in percent (e.g., 100 indicates an increase by 100 percent within the specified time window). + +### Time Window Length (Seconds) + +Specifies the size of the time window in seconds. + +## Output + +Outputs the events if there is a trend observed according to the configuration defined. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listcollector.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listcollector.md new file mode 100644 index 000000000..f789c1cf0 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listcollector.md @@ -0,0 +1,51 @@ +--- +id: org.apache.streampipes.processors.siddhi.listcollector +title: List Collector +sidebar_label: List Collector +--- + + + + + +*** + +## Description + +Collects all values from a field within a specified batch window into a list. + +*** + +## Required input + +Does not have any specific input requirements. + +*** + +## Configuration + +### Field + +The field where values should be collected into a list. + +### Batch Window Size + +The batch window size. + +## Output \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listfilter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listfilter.md new file mode 100644 index 000000000..2526408c8 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.listfilter.md @@ -0,0 +1,53 @@ +--- +id: org.apache.streampipes.processors.siddhi.listfilter +title: List Filter +sidebar_label: List Filter +--- + + + + + +

+ +

+ +*** + +## Description + +Detects the increase of a numerical field over a customizable time window. Example: A temperature value increases by 10 percent within 5 minutes. + +*** + +## Required input + + +*** + +## Configuration + +Describe the configuration parameters here + +### 1st parameter + + +### 2nd parameter + +## Output \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.numericalfilter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.numericalfilter.md new file mode 100644 index 000000000..fb20b1013 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.numericalfilter.md @@ -0,0 +1,63 @@ +--- +id: org.apache.streampipes.processors.siddhi.numericalfilter +title: Numerical Filter (Siddhi) +sidebar_label: Numerical Filter (Siddhi) +--- + + + + + +

+ +

+ +*** + +## Description +The Numerical Filter processor filters numerical values based on a given threshold. Therefore, it uses the lightweight +CEP engine Siddhi by issuing a Siddhi query, e.g. + +``` +// filter query to filter out all events not satisfying the condition +from inputStreamName[numberField<10] +select * +``` + +*** + +## Required input +The processor works with any input event that has one field containing a numerical value. + +*** + +## Configuration + +### Field +Specifies the field name where the filter operation should be applied on. + + +### Operation +Specifies the filter operation that should be applied on the field. + +### Threshold value +Specifies the threshold value. + +## Output +The processor outputs the input event if it satisfies the filter expression. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.topk.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.topk.md new file mode 100644 index 000000000..beb0d8851 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.siddhi.topk.md @@ -0,0 +1,53 @@ +--- +id: org.apache.streampipes.processors.siddhi.topk +title: Top k +sidebar_label: Top k +--- + + + + + +

+ +

+ +*** + +## Description + +Detects the increase of a numerical field over a customizable time window. Example: A temperature value increases by 10 percent within 5 minutes. + +*** + +## Required input + + +*** + +## Configuration + +Describe the configuration parameters here + +### 1st parameter + + +### 2nd parameter + +## Output \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.chunker.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.chunker.md new file mode 100644 index 000000000..3366fd431 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.chunker.md @@ -0,0 +1,69 @@ +--- +id: org.apache.streampipes.processors.textmining.jvm.chunker +title: Chunker (English) +sidebar_label: Chunker (English) +--- + + + + + +

+ +

+ +*** + +## Description + +Segments given tokens into chunks (e.g. noun groups, verb groups, ...) and appends the found chunks to the stream. + +*** + +## Required input + +Needs a stream with two string list properties: +1. A list of tokens +2. A list of part-of-speech tags (the Part-of-Speech processing element can be used for that) + +*** + +## Configuration + +Assign the tokens and the part of speech tags to the corresponding stream property. +To use this component you have to download or train an openNLP model: +https://opennlp.apache.org/models.html + +## Output + +**Example:** + +Input: +``` +tokens: ["John", "is", "a", "Person"] +tags: ["NNP", "VBZ", "DT", "NN"] +``` + +Output: +``` +tokens: ["John", "is", "a", "Person"] +tags: ["NNP", "VBZ", "DT", "NN"] +chunks: ["John", "is", "a Person"] +chunkType: ["NP", "VP", "NP"]) +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.namefinder.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.namefinder.md new file mode 100644 index 000000000..a4c070534 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.namefinder.md @@ -0,0 +1,65 @@ +--- +id: org.apache.streampipes.processors.textmining.jvm.namefinder +title: Name Finder +sidebar_label: Name Finder +--- + + + + + +

+ +

+ +*** + +## Description + +Loads a trained model which finds names like locations or organizations. + +A list of trained models can be found here: http://opennlp.sourceforge.net/models-1.5/.\ +A guide on how to train a new model can be found here: https://opennlp.apache.org/docs/1.9.1/manual/opennlp.html#tools.namefind.training. + +*** + +## Required input + +A stream with a list of tokens from a text. + +*** + +## Configuration + +Configure the Name finder so that the tokens are assigned to the "List of Tokens" property + + +#### Model parameter + +The trained model which should be used to find the names. + +## Output + +Appends a string list property to the stream which contains all found names. + +**Example (with an loaded english person-name-model):** + +Input: `(tokens: ["Hi", "John", "Doe", "is", "here"])` + +Output: `(tokens: ["Hi", "John", "Doe", "is", "here"], foundNames: ["John Doe"])` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.partofspeech.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.partofspeech.md new file mode 100644 index 000000000..13b301128 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.partofspeech.md @@ -0,0 +1,62 @@ +--- +id: org.apache.streampipes.processors.textmining.jvm.partofspeech +title: Part of Speech (English) +sidebar_label: Part of Speech (English) +--- + + + + + +

+ +

+ +*** + +## Description + +Takes in a stream of tokens and marks each token with a part-of-speech tag +The list of used suffixes can be found [here](https://www.ling.upenn.edu/courses/Fall_2003/ling001/penn_treebank_pos.html) + +*** + +## Required input + +A stream with a list property which contains the tokens. + +*** + +## Configuration + +Simply assign the correct output of the previous stream to the part of speech detector input. +To use this component you have to download or train an openNLP model: +https://opennlp.apache.org/models.html + +## Output + +Appends two list properties to the stream: +1. String list: The tag for each token +2. Double list: The confidence for each tag that it is indeed the given tag (between 0 and 1) + +**Example:** + +Input: `(tokens: ["Hi", "Joe"])` + +Output: `(tokens: ["Hi", "Joe"], tags: ["UH", "NNP"], confidence: [0.82, 0.87])` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.sentencedetection.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.sentencedetection.md new file mode 100644 index 000000000..b57ade01a --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.sentencedetection.md @@ -0,0 +1,59 @@ +--- +id: org.apache.streampipes.processors.textmining.jvm.sentencedetection +title: Sentence Detection (English) +sidebar_label: Sentence Detection (English) +--- + + + + + +

+ +

+ +*** + +## Description + +Detects sentences in a text and splits the text accordingly. Only works with english sentences. + +*** + +## Required input + +A stream with a string property which contains a text. + +*** + +## Configuration + +Simply assign the correct output of the previous stream to the tokenizer input. +To use this component you have to download or train an openNLP model: +https://opennlp.apache.org/models.html + +## Output + +Creates for each sentence in a text a new event in which it replaces the text with the sentence. + +**Example:** + +Input: `(text: "Hi, how are you? I am fine!")` + +Output: `(text: "Hi, how are you?")`, `(text: "I am fine!")` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.tokenizer.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.tokenizer.md new file mode 100644 index 000000000..afa55c633 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.textmining.jvm.tokenizer.md @@ -0,0 +1,59 @@ +--- +id: org.apache.streampipes.processors.textmining.jvm.tokenizer +title: Tokenizer (English) +sidebar_label: Tokenizer (English) +--- + + + + + +

+ +

+ +*** + +## Description + +Segments a given text into Tokens (usually words, numbers, punctuations, ...). Works best with english text. + +*** + +## Required input + +A stream with a string property which contains a text. + +*** + +## Configuration + +Simply assign the correct output of the previous stream to the tokenizer input. +To use this component you have to download or train an openNLP model: +https://opennlp.apache.org/models.html + +## Output + +Adds a list to the stream which contains all tokens of the corresponding text. + +**Example:** + +Input: `(text: "Hi, how are you?")` + +Output: `(text: "Hi, how are you?", tokens: ["Hi", ",", "how", "are", "you", "?"])` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.counter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.counter.md new file mode 100644 index 000000000..99649168b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.counter.md @@ -0,0 +1,66 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.booloperator.counter +title: Boolean Counter +sidebar_label: Boolean Counter +--- + + + + + +

+ +

+ +*** + +## Description + +This processor monitors a boolean value and counts how often the value of the boolean changes. +A user can configure whether the changes from FALSE to TRUE, TRUE to FALSE, or BOTH changes should be counted. + +*** + +## Required input + +A boolean value is required in the data stream and can be selected with the field mapping. + +### Boolean Field + +The boolean value to be monitored. + +*** + +## Configuration + +A user can configure whether the changes from TRUE to FALSE, FALSE to TRUE, or all changes of the +boolean value should be counted. + +### Flank parameter + +Either: +* TRUE -> FALSE: Increase counter on a true followed by a false +* FALSE -> TRUE: Increase counter on a false followed by a true +* BOTH: Increase counter on each change of the boolean value on two consecutive events + +## Output + +Adds an additional numerical field with the current count value to the event. +Events are just emitted when the counter changes. +Runtime Name: countField \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.inverter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.inverter.md new file mode 100644 index 000000000..07ccaf2ff --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.inverter.md @@ -0,0 +1,51 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.booloperator.inverter +title: Boolean Inverter +sidebar_label: Boolean Inverter +--- + + + + + +

+ +

+ +*** + +## Description + +This processor requires a boolean value in the data stream and inverts its value. (e.g. true -> flase) + +*** + +## Required input + +### Boolean Field + +The boolean value to be inverted. + +*** + +## Configuration +No further configuration required + +## Output +The output schema is the same as the input schema. Just the value of the property is changed. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.logical.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.logical.md new file mode 100644 index 000000000..9cf0d921b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.logical.md @@ -0,0 +1,42 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.booloperator.logical +title: Boolean Logical Operator +sidebar_label: Boolean Logical Operator +--- + + + + + +## Description + +This processor performs the logical boolean operation b/w the vales of set of properties. +A user can select the type of operation and set of properties. + +*** + +## Required input + +Type of logical boolean operator and set of properties on which operator needs to perform. + +*** + +## Output + +Outputs the incoming event while appending the result (``boolean-operations-result``) to the incoming event. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timekeeping.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timekeeping.md new file mode 100644 index 000000000..ff3536006 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timekeeping.md @@ -0,0 +1,70 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.booloperator.timekeeping +title: Measure Time Between Two Sensors +sidebar_label: Measure Time Between Two Sensors +--- + + + + + +

+ +

+ +*** + +## Description + +This processor can be used to measure the time between two boolean sensors. +For example on a conveyor, where one sensor is placed on the left and one senor placed on the right. +Parts are transported on the conveyor and the sensors are boolean sensors detecting those parts. +The time is measured between the two sensors as well as the amount of complete transportation's is counted. +The measurement is initialized once the left sensor is true and stopped once the right sensor is true. +There can also be multiple parts on the conveyor as long as the individual parts do not change. + + +

+ +

+ +*** + +## Required input +Requires two boolean fields in the datastream. + +### Left Field +The left field starts the timer when value is true. + +### Right Field +The right field stops the timer and emits the event when its value is true. + +*** + +## Configuration +No furhter configuration is required. + +## Output +Appends two fields to the input event. + +### Timer Field +The timer field is a numeric value representing the time between the two sensors. Runtime name: measured_time + +### Counter +The counter indicated how many events where emitted by this component. Runtime name: counter \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timer.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timer.md new file mode 100644 index 000000000..044810417 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timer.md @@ -0,0 +1,58 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.booloperator.timer +title: Boolean Timer +sidebar_label: Boolean Timer +--- + + + + + +

+ +

+ +*** + +## Description + +This processor measures how long a boolean value does not change. +Once the value is changes the event with the measured time is emitted. + + +*** + +## Required input + +A boolean value is required in the data stream. + +### Field + +The boolean field which is monitored for state changes. + +*** + +## Configuration + +### Timer value +Define whether it should be measured how long the value is true or how long the value is false. + +## Output +Appends a field with the time how long the value did not change. +Is emitted on the change of the boolean value. Runtime name: measured_time diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.changed-value.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.changed-value.md new file mode 100644 index 000000000..9b5f1ac10 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.changed-value.md @@ -0,0 +1,46 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.changed-value +title: Value Changed +sidebar_label: Value Changed +--- + + + + + +

+ +

+ +*** + +## Description + +This processor sends out an event everytime a specific object changes. +It also adds a timestamp in ms from the system time. + +*** + +## Configuration +Select property to monitor for changes + +Describe the configuration parameters here + +## Output +Emit an event on change and append a timestamp when the change occured \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.count-array.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.count-array.md new file mode 100644 index 000000000..b32bb7823 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.count-array.md @@ -0,0 +1,55 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.count-array +title: Count Array +sidebar_label: Count Array +--- + + + + + +

+ +

+ +*** + +## Description + +This processor takes a list field, computes the size of the list and appends the result to the event. + +*** + +## Required input + +This processor works with any event that has a field of type ``list``. + +*** + +## Configuration + +Describe the configuration parameters here + +### List Field + +The field containing the list that should be used. + +## Output + +Outputs the incoming event while appending the list size (named ``countValue``) to the incoming event. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.csvmetadata.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.csvmetadata.md new file mode 100644 index 000000000..9c4871a5d --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.csvmetadata.md @@ -0,0 +1,77 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.csvmetadata +title: CSV Metadata Enricher +sidebar_label: CSV Metadata Enricher +--- + + + + +Enrich a datastream with information provided in a CSV file. +The data of the CSV file is matched by an id column with a property value of a String in the data stream. + +*** + +## Description +Upload a CSV file with static meta information that will be appended to each event. +The file can contain different information for different keys in the stream. + + +### Structure of CSV file +The first row containes the runtime names for the properties to insert. +Once the file is uploaded the user can select which column to use for the matching property +and which values should be appended. +Delimiter: ';' + + +*** + +## Example +Add the location of a production line to the event + +### Input event +``` +{ + 'line_id': 'line1', + 'timestamp': 1586378041 +} +``` + +### CSV File +``` +production_line;location +line1;germany +line2;uk +line3;usa +``` + +### Configuration +* The field that is used for the lookup (Example: line_id) +* The CSV file (Example: Upload the csv file) +* Field to match (Example: production_line) +* Fields to append (Example: location) + +### Output event +``` +{ + 'line_id': 'line1', + 'timestamp': 1586378041, + 'location': 'germany' +} +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.datetime.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.datetime.md new file mode 100644 index 000000000..4b237cd43 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.datetime.md @@ -0,0 +1,78 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.datetime +title: Datetime From String +sidebar_label: Datetime From String +--- + + + + + +

+ +

+ +*** + +## Overview + +The "Datetime From String" processor is a handy tool that helps convert human-readable datetime information into a +format that machines can understand. This is particularly useful when dealing with data that includes dates and times. + +### Why Use This Processor? + +In the context of event streams, you may encounter dates and times formatted for human readability but not necessarily +optimized for computer processing. The "Datetime From String" processor addresses this by facilitating the conversion +of human-readable datetime information within your continuous stream of events. + +*** + +## How It Works + +When you input a data stream into this processor containing a datetime in a specific format (such as "2023-11-24 15:30: +00"), it +undergoes a transformation. The processor converts it into a computer-friendly format called a ZonedDateTime object. + +### Example + +Let's say you have an event stream with a property containing values like "2023-11-24 15:30:00" and you want to make +sure your computer understands it. You can use +this processor to convert it into a format that's machine-friendly. + +*** + +## Getting Started + +To use this processor, you need one thing in your data: + +1. **Datetime String**: This is the name of the event property that contains the human-readable datetime string, like "2023-11-24 15:30:00". + + +### Configuration + +The only thing you need to configure is the time zone. +1. **Time Zone**: Specify the time zone that applies to your datetime if it doesn't already have this information.This ensures that the processor understands the context of your +datetime. + +## Output + +After the conversion happens, the processor adds a new piece of information to your data stream: + +* **timestringInMillis**: This is the transformed datetime in a format that computers can easily work with (UNIX timestamp in milliseconds). +* **timeZone**: The name of the timezone the `dateTime` value refers to. Can be used to reconstitute the actual time. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.duration-value.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.duration-value.md new file mode 100644 index 000000000..0d1a66b2e --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.duration-value.md @@ -0,0 +1,51 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.duration-value +title: Calculate Duration +sidebar_label: Calculate Duration +--- + + + + + +

+ +

+ +*** + +## Description + +This processor calculates the duration for a given stream with a start timestamp and an end timestamp. + +*** + +## Required input +Two timestamp fields + +*** + +## Configuration + +* Start Timestamp: The first timestamp (t1) +* End Timestamp: The second timestamp (t2) +* Time Unit of the result + +## Output +Appends a new field with the difference of t2 and t1 \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.field-mapper.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.field-mapper.md new file mode 100644 index 000000000..b903eb206 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.field-mapper.md @@ -0,0 +1,74 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.field-mapper +title: Field Mapper +sidebar_label: Field Mapper +--- + + + + + +

+ +

+ +*** + +## Description + +Replaces one or more fields with a new field and computes a hash value of these fields + +*** + +## Configuration + +* Fields: Fields that will be mapped into a property +* Name of the new field + +*** + +## Example + +Merge two fields into a hash value + +### Input event + +``` +{ + "timestamp":1586380104915, + "mass_flow":4.3167, + "temperature":40.05, + "sensorId":"flowrate01" +} +``` + +### Configuration + +* Fields: mass_flow, temperature +* Name of new field: demo + +### Output event + +``` +{ + "timestamp":1586380104915, + "sensorId":"flowrate01" + "demo":"8ae11f5c83610104408d485b73120832", +} +``` \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldhasher.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldhasher.md new file mode 100644 index 000000000..b4b0d6903 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldhasher.md @@ -0,0 +1,55 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.fieldhasher +title: Field Hasher +sidebar_label: Field Hasher +--- + + + + + +

+ +

+ +*** + +## Description + +The Field Hasher uses an algorithm to encode values in a field. +The Field Hasher can use MD5, SHA1 or SHA2 to hash field values. + +*** + +## Required input +This processor requires at least one field of type string. + +*** + +## Configuration + +### Field +Specifies the string field that will be encoded. + +### Hash Algorithm +Specifies the algorithm used to encode the string field. The following algorithms +are available: SHA2, MD5 or SHA1. + +## Output +The encoded string field. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldrename.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldrename.md new file mode 100644 index 000000000..8d8e463e3 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.fieldrename.md @@ -0,0 +1,59 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.fieldrename +title: Field Renamer +sidebar_label: Field Renamer +--- + + + + + + +*** + +## Description + +Replaces the runtime name of an event property with a custom defined name. +Useful for data ingestion purposes where a specific event schema is required. + + +*** + +### OldFieldName +Specifies the field to rename. + +### NewFieldName +Specifies the new runtime name of the field. + +## Output +Example: + +Old Output: +``` +{ + 'timestamp': 16003000, +} +``` + +New Ouput: +``` +{ + 'time': 16003000, +} +``` \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.measurementunitconverter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.measurementunitconverter.md new file mode 100644 index 000000000..303309c42 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.measurementunitconverter.md @@ -0,0 +1,53 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.measurementunitconverter +title: Measurement Unit Converter +sidebar_label: Measurement Unit Converter +--- + + + + + +

+ +

+ +*** + +## Description + +Converts a unit of measurement to another one. + +*** + +## Required input + + +*** + +## Configuration + +Describe the configuration parameters here + +### 1st parameter + + +### 2nd parameter + +## Output \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.edge.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.edge.md new file mode 100644 index 000000000..09c0a4489 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.edge.md @@ -0,0 +1,58 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.processor.booloperator.edge +title: Signal Edge Filter +sidebar_label: Signal Edge Filter +--- + + + + + +

+ +

+ +*** + +## Description + +Observes a boolean value and forwards the event when a signal edge is detected + +*** + +## Required input + +### Boolean Field +Boolean field that is observed + +*** + +## Configuration +### Kind of edge +* Detect rising edges +* Detect falling edges +* Detect both + +### Delay +Defines for how many events the signal must be stable before result is emitted. +(E.g. if set to 2, the result is not emitted if value toggles between true and false, +it fires when two consecutive events are detected after the flank) + +## Output +Emits input event, when the signal edge is detected diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.state.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.state.md new file mode 100644 index 000000000..7eb96ae8b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.state.md @@ -0,0 +1,63 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.processor.booloperator.state +title: Boolean To State +sidebar_label: Boolean To State +--- + + + + + +

+ +

+ +*** + +## Description + +Converts boolean fields to a state string representing the current state of the system. +This processor requires one or multiple boolean values in the data stream. +For the selected value which is true, the runtime name is added as the state field. +*** + +## Required input + +### Boolean Fields + +Boolean fields that are converted to the state when true + +### Default State + +When all boolean values are false, a default state can be defined + +### Mapping Configuration + +Configuration to provide a string mapping for each possible value. +On the left ist the value of the runtime name and on the right the new value (e.g. {"runtimeName": "newValue"}). + +*** + +## Configuration + +No further configuration required + +## Output + +The output contains a new value with the string values of the state diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.state.labeler.number.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.state.labeler.number.md new file mode 100644 index 000000000..021bee3c8 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.state.labeler.number.md @@ -0,0 +1,58 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.processor.state.labeler.number +title: Number Labeler +sidebar_label: Number Labeler +--- + + + + + +

+ +

+ +*** + +## Description + +Apply a rule to a value of a field. (E.g. when minimum value is lower then 10, add label `not ok` else add label `ok`) + +*** + +## Required input + +Requires a sensor value + +### Sensor value + +A number representing the current sensor value. + +*** + +## Configuration + +### Condition +Define a rule which label to add. Example: `<;5;nok` means when the calculated value is smaller then 5 add label ok. +The default label can be defined with `*;nok`. +The first rule that is true defines the label. Rules are applied in the same order as defined here. + + +## Output +Appends a new field with the label defined in the Condition Configuration diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.staticmetadata.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.staticmetadata.md new file mode 100644 index 000000000..6400ed659 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.staticmetadata.md @@ -0,0 +1,74 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.processor.staticmetadata +title: Static Metadata Enricher +sidebar_label: Static Metadata Enricher +--- + + + + +Enrich a data stream by dynamically adding fields based on user-provided static metadata configuration. + +--- + +## Description + +The Static Metadata Enricher is designed to enrich a data stream by dynamically adding fields based on user-provided +metadata configuration. Users can specify static properties, and the processor will process each event, adding fields +according to the provided key-value pairs. The output strategy is determined dynamically based on the provided metadata. +For added convenience, users also have the option of uploading a CSV file with metadata information. + +### Configuration + +For each metadata entry, configure the following three options: + +- **Runtime Name:** A unique identifier for the property during runtime. +- **Value:** The value associated with the property. +- **Data Type:** The data type of the property value. + +#### Using CSV Option + +Alternatively, you can utilize the CSV upload feature by creating a CSV file with the following format: + +``` +Runtime Name,Runtime Value,Data Type +sensorType,Temperature,String +maxSensorValue,100.0,Float +minSensorValue,0,Float +``` + +## Example +### Input Event + +```json +{ + "reading": 25.5 +} +``` + +### Output Event + +```json +{ + "reading": 25.5, + "sensorType": "Temperature", + "maxSensorValue": 100.0, + "minSensorValue": 0.0 +} +``` diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.stringoperator.state.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.stringoperator.state.md new file mode 100644 index 000000000..02fbfa2cb --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.stringoperator.state.md @@ -0,0 +1,51 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.processor.stringoperator.state +title: String To State +sidebar_label: String To State +--- + + + + + +

+ +

+ +*** + +## Description + +Convert string fields to a state representing the current state of the system. +This processor requires one or multiple string values in the data stream. +For each of the selected values is added to the states field. +*** + +## Required input + +### String Fields +String fields that are added to the state array + +*** + +## Configuration +No further configuration required + +## Output +The output contains a new value with the string values of the state diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.timestampextractor.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.timestampextractor.md new file mode 100644 index 000000000..6aa99cfed --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.processor.timestampextractor.md @@ -0,0 +1,58 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.processor.timestampextractor +title: Timestamp Extractor +sidebar_label: Timestamp Extractor +--- + + + + + +

+ +

+ +*** + +## Description + +This processor extracts a timestamp into the individual time fields (e.g. day field, hour field, ....) + +*** + +## Required input + +This processor requires an event that provides a timestamp value (a field that is marked to be of type ``http://schema +.org/DateTime``. + +*** + +## Configuration + +### Timestamp Field + +The field of the event containing the timestamp to parse. + +### Extract Fields + +Select the individual parts of the timestamp that should be extracted, e.g., Year, Minute and Day. + +## Output + +The output of this processor is a new event that contains the fields selected by the ``Extract Fields`` parameter. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.round.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.round.md new file mode 100644 index 000000000..3913f9fe5 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.round.md @@ -0,0 +1,72 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.round +title: Numeric Rounding +sidebar_label: Numeric Rounding +--- + + + + + +

+ +

+ +*** + +## Description + +This processor rounds numeric values to the given decimal places. +It supports multiple rounding strategies. + +*** + +## Required input + +This processor requires an event that provides numerical properties. + +*** + +## Configuration + +### Fields to Be Rounded + +Select which fields of the event should be rounded. + +### Number of Digits + +Specify the number of digits after the decimal point to round/keep, e.g., if number is 2.8935 and 'digits' is 3, +the result will be 2.894. + +### Mode of Rounding + +Specify the mode of rounding. +Supported rounding modes: +* `UP`: Rounding mode to round away from zero. Always increments the digit prior to a non-zero discarded fraction. Note that this rounding mode never decreases the magnitude of the calculated value. +* `DOWN`: Rounding mode to round towards zero. Never increments the digit prior to a discarded fraction (i.e., truncates). Note that this rounding mode never increases the magnitude of the calculated value. +* `CEILING`: Rounding mode to round towards positive infinity. If the result is positive, behaves as for `UP`; if negative, behaves as for `DOWN`. Note that this rounding mode never decreases the calculated value +* `FLOOR`: Rounding mode to round towards negative infinity. If the result is positive, behave as for `DOWN`; if negative, behave as for `UP`. Note that this rounding mode never increases the calculated value. +* `HALF_UP`: Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. Behaves as for `UP` if the discarded fraction is ≥ 0.5; otherwise, behaves as for `DOWN`. +* `HALF_DOWN`: Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. Behaves as for `UP` if the discarded fraction is > 0.5; otherwise, behaves as for `DOWN`. +* `HALF_EVEN`: Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor. Behaves as for `HALF_UP` if the digit to the left of the discarded fraction is odd; behaves as for `HALF_DOWN` if it's even. Note that this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations. + +## Output + +The output of this processor is the same event with the fields selected by the ``Fiels to Be Rounded`` parameter rounded +to ``Number of Digits`` digits. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.split-array.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.split-array.md new file mode 100644 index 000000000..47e0b6d08 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.split-array.md @@ -0,0 +1,60 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.split-array +title: Split Array +sidebar_label: Split Array +--- + + + + + +

+ +

+ +*** + +## Description + +This processor takes an array of event properties and creates an event for each of them. +Further property of the events can be added to each element. + +*** + +## Required input + +This processor works with any event that has a field of type ``list``. + +*** + +## Configuration + +### Keep Fields + +Fields of the event that should be kept in each resulting event. + +### List field + +The name of the field that contains the list values that should be split. + + +## Output + +This data processor produces an event with all fields selected by the ``Keep Fields`` parameter and all fields of the + selected list field. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.counter.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.counter.md new file mode 100644 index 000000000..2b5b1a725 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.counter.md @@ -0,0 +1,65 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.stringoperator.counter +title: String Counter +sidebar_label: String Counter +--- + + + + + +

+ +

+ +*** + +## Description + +This processor monitors a string field and counts how often the value of the string changes. +Hereby, a change is characterized by +the value of the field before and the value after the change, combined forming a pair. +The processor keeps track of the counter for each pair. + +*** + +## Required input + +A string field is required in the data stream and can be selected with the field mapping. + +### String Field + +The string field to be monitored. + +*** + +## Configuration + +(no further configuration required) + +## Output + +The following three fields are appended to the event: + +* [counter] numerical field with the current count value for the given value pair +* [change_from] the value of the string before the change +* [change_to] the value of the string after the change + +The event is emitted whenever the value of the string field changes. + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.timer.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.timer.md new file mode 100644 index 000000000..6ce9c78ad --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.timer.md @@ -0,0 +1,66 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.stringoperator.timer +title: String Timer +sidebar_label: String Timer +--- + + + + + +

+ +

+ +*** + +## Description + +This processor measures how long a value of a string field does not change. +Once the value is changes the event with the measured time and the corresponding string value is emitted. + + +*** + +## Required input + +A string field is required in the data stream. + +### Field + +The string field which is monitored for any value changes. + + +*** + +## Configuration + +### Output Frequency + +Define when an event should be emitted, either on each input event or just when the string value changes. + +## Output + +The following two fields are appended to the event: + +* [measured_time] the measured time for the string value to not change +* [field_value] the corresponding string value + +The event is emitted whenever the value of the string field changes. + diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.taskduration.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.taskduration.md new file mode 100644 index 000000000..245339056 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.taskduration.md @@ -0,0 +1,50 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.taskduration +title: Task Duration +sidebar_label: Task Duration +--- + + + + + + +*** + +## Description + +This processors computes the duration of a task, i.e., a field containing a task description. It outputs an event + every time this task value changes and computes the duration between the first occurrence of this task and the + current event. For instance, you can use this event to calculate the time a specific process step requires. +*** + +## Required input + +A timestamp value is required and a field containing a task value. + +*** + +## Configuration + +(no further configuration required) + +## Output + +Emits an event that contains the process step, built from the names of the first task identifier and the identifier + of the subsequent task. In addition, the duration is part of the output event, provided in milliseconds. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.transform-to-boolean.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.transform-to-boolean.md new file mode 100644 index 000000000..8195cb36b --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.processors.transformation.jvm.transform-to-boolean.md @@ -0,0 +1,53 @@ +--- +id: org.apache.streampipes.processors.transformation.jvm.transform-to-boolean +title: Transform to boolean +sidebar_label: Transform to boolean +--- + + + + + +

+ +

+ +*** + +## Description + +This processors transforms numbers and strings to boolean values. + + +*** + +## Required input + +A string with the values "true", "True", "false", "False" or a number with value 1.0, 1, 0, or 0.0 + +*** + +## Configuration + +Select fields that should be converted to boolean. + +## Output + +Selected properties of input events are transformed to booleans. +When the value is not valid an error message is logged and the event is discarde. \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.bufferrest.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.bufferrest.md new file mode 100644 index 000000000..d1ddcf1ac --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.bufferrest.md @@ -0,0 +1,58 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.bufferrest +title: Buffered REST Publisher +sidebar_label: Buffered REST Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Collects a given amount of events into a JSON array. Once this event count is reached +the JSON array is posted to the given REST interface. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### REST URL + +The complete URL of the REST endpoint. + +### Buffer Size + +The amount of events before sending. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.jms.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.jms.md new file mode 100644 index 000000000..9116688c9 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.jms.md @@ -0,0 +1,60 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.jms +title: JMS Publisher +sidebar_label: JMS Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes events to a message broker (e.g., ActiveMQ) using the Java Message Service (JMS) protocol. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### JMS Broker Settings + +The basic settings to connect to the broker. +The JMS broker URL indicates the URL of the broker (e.g., tcp://localhost), the port indicates the port of the broker + (e.g., 61616) + + +### JMS Topic + +The topic where events should be sent to. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.kafka.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.kafka.md new file mode 100644 index 000000000..3fa00b57e --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.kafka.md @@ -0,0 +1,61 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.kafka +title: Kafka Publisher +sidebar_label: Kafka Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes events to Apache Kafka. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Kafka Broker Settings + +The basic settings to connect to the broker. +The Kafka broker URL indicates the URL of the broker (e.g., localhost), the port indicates the port of the broker + (e.g., 9092) + + +### Kafka Topic + +The topic where events should be sent to. + + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.mqtt.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.mqtt.md new file mode 100644 index 000000000..962082c26 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.mqtt.md @@ -0,0 +1,61 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.mqtt +title: MQTT Publisher +sidebar_label: MQTT Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes events to MQTT. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### MQTT Broker Settings + +The basic settings to connect to the broker. +The MQTT broker URL indicates the URL of the broker (e.g., localhost), the port indicates the port of the broker +(e.g., 1883) + + +### MQTT Topic + +The topic where events should be sent to. + + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.nats.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.nats.md new file mode 100644 index 000000000..7f1c932f1 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.nats.md @@ -0,0 +1,78 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.nats +title: NATS Publisher +sidebar_label: NATS Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes events to NATS broker. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### NATS Subject + +The subject (topic) where events should be sent to. + +### NATS Broker URL + +The URL to connect to the NATS broker. It can be provided multiple urls separated by commas(,). + (e.g., nats://localhost:4222,nats://localhost:4223) + +### Username + +The username to authenticate the client with NATS broker. + +It is an optional configuration. + +### NATS Broker URL + +The password to authenticate the client with NATS broker. + +It is an optional configuration. + +### NATS Connection Properties + +All other possible connection configurations that the nats client can be created with. +It can be provided as key value pairs separated by colons(:) and commas(,). + (e.g., io.nats.client.reconnect.max:1, io.nats.client.timeout:1000) + +It is an optional configuration. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.pulsar.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.pulsar.md new file mode 100644 index 000000000..ea444d595 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.pulsar.md @@ -0,0 +1,63 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.pulsar +title: Pulsar Publisher +sidebar_label: Pulsar Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes events to Apache Pulsar. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Pulsar Broker Hostname + +The hostname to connect to the broker. + +### Pulsar Broker Port + +The port to connect to the broker (e.g., 6650) + + +### Pulsar Topic + +The topic where events should be sent to. + + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rabbitmq.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rabbitmq.md new file mode 100644 index 000000000..0da1aa89f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rabbitmq.md @@ -0,0 +1,73 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.rabbitmq +title: RabbitMQ Publisher +sidebar_label: RabbitMQ Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Forwards events to a RabbitMQ broker + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Host + +The hostname of the RabbitMQ broker. + +### Port + +The port of the RabbitMQ broker. + +### User + +The username used to connect to the RabbitMQ broker. + +### Password + +The password used to connect to the RabbitMQ broker. + +### Exchange Name + +The name of the exchange. + +### RabbitMQ Topic + +The topic where events should be sent to. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rest.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rest.md new file mode 100644 index 000000000..71d792398 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rest.md @@ -0,0 +1,52 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.rest +title: REST Publisher +sidebar_label: REST Publisher +--- + + + + +

+ +

+ +*** + +## Description + +Posts a JSON representation of an event to a REST interface. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### REST URL + +The complete URL of the REST endpoint. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rocketmq.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rocketmq.md new file mode 100644 index 000000000..a3e0e27b1 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.rocketmq.md @@ -0,0 +1,59 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.rocketmq +title: RocketMQ Publisher +sidebar_label: RocketMQ Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes events to Apache RocketMQ. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### RocketMQ Endpoint + +The endpoint to connect to the broker. + + +### RocketMQ Topic + +The topic where events should be sent to. + + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.tubemq.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.tubemq.md new file mode 100644 index 000000000..34dc0222f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.tubemq.md @@ -0,0 +1,61 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.tubemq +title: TubeMQ (InLong) Publisher +sidebar_label: TubeMQ (InLong) Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publishes events to Apache TubeMQ (InLong). + +*** + +## Required Inputs + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### TubeMQ Master Information + +This field describes the endpoints of all the TubeMQ masters. + +The format should be like `ip1:port1,ip2:port2,ip3:port3`. + + +### TubeMQ Topic + +The topic where events should be sent to. + + +## Output + +(not applicable for data sinks) diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.websocket.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.websocket.md new file mode 100644 index 000000000..80706526c --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.brokers.jvm.websocket.md @@ -0,0 +1,52 @@ +--- +id: org.apache.streampipes.sinks.brokers.jvm.websocket +title: Websocket Server +sidebar_label: Websocket Server +--- + + + + +*** + +

+ +

+ +## Description + +Send a message to a connected websocket client + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Port + +The port on which the websocket listens for connections + +## Output + +(not applicable for data sinks) diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.ditto.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.ditto.md new file mode 100644 index 000000000..0726af127 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.ditto.md @@ -0,0 +1,73 @@ +--- +id: org.apache.streampipes.sinks.databases.ditto +title: Eclipse Ditto +sidebar_label: Eclipse Ditto +--- + + + + + +

+ +

+ +*** + +## Description + +Forwards events to the Eclipse Ditto API. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Fields to send + +The fields that should be stored as a property to Ditto endpoint. + +### Ditto API endpoint + +The endpoint URL of the Ditto instance. + +### Username + +The username to authenticate the Ditto endpoint. + +### Password + +The password to authenticate the Ditto endpoint. + +### Thing ID + +The Ditto thing ID. + +#### Feature ID + +The Ditto feature ID + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.couchdb.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.couchdb.md new file mode 100644 index 000000000..eb84223bb --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.couchdb.md @@ -0,0 +1,63 @@ +--- +id: org.apache.streampipes.sinks.databases.jvm.couchdb +title: CouchDB +sidebar_label: CouchDB +--- + + + + + +

+ +

+ +*** + +## Description + +Stores events in an Apache CouchDB database. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +Describe the configuration parameters here + +### Hostname + +The hostname of the CouchDB instance. + +### Port + +The port of the CouchDB instance. + +### Database Name + +The name of the database where events will be stored + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.influxdb.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.influxdb.md new file mode 100644 index 000000000..f712edec6 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.influxdb.md @@ -0,0 +1,85 @@ +--- +id: org.apache.streampipes.sinks.databases.jvm.influxdb +title: InfluxDB +sidebar_label: InfluxDB +--- + + + + + +

+ +

+ +*** + +## Description + +Stores events in an InfluxDB. + +*** + +## Required input + +This sink requires an event that provides a timestamp value (a field that is marked to be of type ``http://schema +.org/DateTime``. + +*** + +## Configuration + +### Hostname + +The hostname/URL of the InfluxDB instance. (Include http(s)://). + +### Port + +The port of the InfluxDB instance. + +### Database Name + +The name of the database where events will be stored. + +### Measurement Name + +The name of the Measurement where events will be stored (will be created if it does not exist). + +### Username + +The username for the InfluxDB Server. + +### Password + +The password for the InfluxDB Server. + +### Timestamp Field + +The field which contains the required timestamp. + +### Buffer Size + +Indicates how many events are written into a buffer, before they are written to the database. + +### Maximum Flush + +The maximum waiting time for the buffer to fill the Buffer size before it will be written to the database in ms. +## Output + +(not applicable for data sinks) diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.iotdb.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.iotdb.md new file mode 100644 index 000000000..777126236 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.iotdb.md @@ -0,0 +1,91 @@ +--- +id: org.apache.streampipes.sinks.databases.jvm.iotdb +title: Apache IoTDB +sidebar_label: Apache IoTDB +--- + + + + + +

+ +

+ +*** + +## Description + +Stores events in a IoTDB database. + +Events will be stored in timeseries `root.${Database Name}.${Device (Entity) Name}.${Event Key}`. + +Please reference to [https://iotdb.apache.org/](https://iotdb.apache.org/) for more information. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Hostname + +The hostname of the IoTDB instance. + +### Port + +The port of the IoTDB instance (default 6667). + +### Username + +The username for the IoTDB Server. + +### Password + +The password for the IoTDB Server. + +### **Database Name** + +The name of the database where events will be stored (will be created if it does not exist). + +A database is a group of devices (entities). Users can create any prefix path as a database. + +### **Device (Entity) Name** + +The name of the device (entity) where events will be stored. + +A device (also called entity) is an equipped with measurements in real scenarios. In IoTDB, all measurements should have +their corresponding devices. + +### **Measurements** + +All keys of fields in an event will be automatically converted to suffixes of timeseries. + +A measurement is information measured by detection equipment in an actual scene and can transform the sensed information +into an electrical signal or other desired form of information output and send it to IoTDB. + +In IoTDB, all data and paths stored are organized in units of measurements. + +## Output + +(not applicable for data sinks) diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.opcua.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.opcua.md new file mode 100644 index 000000000..b2e9cda47 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.opcua.md @@ -0,0 +1,65 @@ +--- +id: org.apache.streampipes.sinks.databases.jvm.opcua +title: OPC-UA +sidebar_label: OPC-UA +--- + + + + + +

+ +

+ +*** + +## Description + +Allows to write events to an OPC-UA server. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Hostname + +The hostname of the OPC-UA server. + +### Port + +The port of the OPC-UA server. + +### Namespace Index + +The namespace index in which the node should be written + +### Node Id + +The node id of the resulting node + +### Number Mapping + +The property of the event that should be written to the OPC-UA server diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.postgresql.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.postgresql.md new file mode 100644 index 000000000..439f9bc65 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.postgresql.md @@ -0,0 +1,73 @@ +--- +id: org.apache.streampipes.sinks.databases.jvm.postgresql +title: PostgreSQL +sidebar_label: PostgreSQL +--- + + + + + +

+ +

+ +*** + +## Description + +Stores events in a Postgres database. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Hostname + +The hostname of the PostgreSQL instance. + +### Port + +The port of the PostgreSQL instance (default 5432). + +### Database Name + +The name of the database where events will be stored + +### Table Name + +The name of the table where events will be stored (will be created if it does not exist) + +### Username + +The username for the PostgreSQL Server. + +### Password + +The password for the PostgreSQL Server. + +## Output + +(not applicable for data sinks) diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.redis.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.redis.md new file mode 100644 index 000000000..cee98444d --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.databases.jvm.redis.md @@ -0,0 +1,87 @@ +--- +id: org.apache.streampipes.sinks.databases.jvm.redis +title: Redis +sidebar_label: Redis +--- + + + + + +

+ +

+ +*** + +## Description + +Stores events in a Redis key-value store. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +Describe the configuration parameters here + +### Hostname +The hostname of the Redis instance + +### Port +The port of the Redis instance (default 6379) + +### Key Field +Runtime field to be used as the key when storing the event. If auto-increment is enabled, this setting will be ignored. + +### Auto Increment +Enabling this will generate a sequential numeric key for every record inserted. (note: enabling this will ignore Key Field) + +### Expiration Time (Optional) +The expiration time for a persisted event. + +### Password (Optional) +The password for the Redis instance. + +### Connection Name (Optional) +A connection name to assign for the current connection. + +### DB Index (Optional) +Zero-based numeric index for Redis database. + +### Max Active (Redis Pool) (Optional) +The maximum number of connections that can be allocated from the pool. + +### Max Idle (Redis Pool) (Optional) +The maximum number of connections that can remain idle in the pool. + +### Max Wait (Redis Pool) (Optional) +The maximum number of milliseconds that the caller needs to wait when no connection is available. + +### Max Timeout (Redis Pool) (Optional) +The maximum time for connection timeout and read/write timeout. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.datalake.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.datalake.md new file mode 100644 index 000000000..44a091864 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.datalake.md @@ -0,0 +1,68 @@ +--- +id: org.apache.streampipes.sinks.internal.jvm.datalake +title: Data Lake +sidebar_label: Data Lake +--- + + + + + +

+ +

+ +*** + +## Description + +Stores events in the internal data lake so that data can be visualized in the live dashboard or in the data explorer. +Simply create a pipeline with a data lake sink, switch to one of the data exploration tool and start exploring your +data! + +*** + +## Required input + +This sink requires an event that provides a timestamp value (a field that is marked to be of type ``http://schema +.org/DateTime``. + +*** + +## Configuration + +### Identifier + +The name of the measurement (table) where the events are stored. + +### Schema Update Options + +The Schema Update Options dictate the behavior when encountering a measurement (table) with the same identifier. + +#### Option 1: Update Schema + +- **Description:** Overrides the existing schema. +- **Effect on Data:** The data remains in the data lake, but accessing old data is restricted to file export. +- **Impact on Features:** Other StreamPipes features, such as the Data Explorer, will only display the new event schema. + +#### Option 2: Extend Existing Schema + +- **Description:** Keeps old event fields in the event schema. +- **Strategy:** This follows an append-only strategy, allowing continued work with historic data. +- **Consideration:** Old properties may exist for which no new data is generated. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.notification.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.notification.md new file mode 100644 index 000000000..f29391c1c --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.internal.jvm.notification.md @@ -0,0 +1,63 @@ +--- +id: org.apache.streampipes.sinks.internal.jvm.notification +title: Notification +sidebar_label: Notification +--- + + + + + +

+ +

+ +*** + +## Description + +Displays a notification in the UI panel of StreamPipes. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +### Notification Title + +The title of the notification. + +### Content + +The notification message. + +### Silent Period + +The *Silent Period* is the duration, expressed in minutes, during which notifications are temporarily disabled after one +has been sent. This feature is implemented to prevent overwhelming the target with frequent notifications, avoiding +potential spam behavior. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.email.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.email.md new file mode 100644 index 000000000..72393be3f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.email.md @@ -0,0 +1,73 @@ +--- +id: org.apache.streampipes.sinks.notifications.jvm.email +title: Email Notification +sidebar_label: Email Notification +--- + + + + + +

+ +

+ +*** + +## Description + +This sink sends an email to a specified receiver. + +Before you use this sink, the settings of your email server need to be configured. +After you've installed the element, navigate to ``Settings``, open the panel ``Sinks Notifications JVM`` and add your +mail server and credentials. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +The following configuration is required: + +### Receiver Address + +The email address of the receiver. + +### Subject + +The subject of the email. + +### Content + +The mail text. + +### Silent Period + +The *Silent Period* is the duration, expressed in minutes, during which notifications are temporarily disabled after one +has been sent. This feature is implemented to prevent overwhelming the target with frequent notifications, avoiding +potential spam behavior. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.msteams.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.msteams.md new file mode 100644 index 000000000..2090564e0 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.msteams.md @@ -0,0 +1,86 @@ +--- +id: org.apache.streampipes.sinks.notifications.jvm.msteams +title: MS Teams Sink +sidebar_label: MS Teams Sink +--- + + + +# MS Teams Sink + +

+ +

+ +--- + + + +The MS Teams Sink is a StreamPipes data sink that facilitates the sending of messages to a Microsoft Teams channel +through a Webhook URL. Whether you need to convey simple text messages or employ more advanced formatting with [Adaptive +Cards](https://adaptivecards.io/), this sink provides a versatile solution for integrating StreamPipes with Microsoft Teams. + +--- + +## Required input + +The MS Teams Sink does not have any specific requirements for incoming event types. It is designed to work seamlessly +with any type of incoming event, making it a versatile choice for various use cases. + +--- + +## Configuration + +#### Webhook URL + +To configure the MS Teams Sink, you need to provide the Webhook URL that enables the sink to send messages to a specific +MS Teams channel. If you don't have a Webhook URL, you can learn how to create +one [here](https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook?tabs=dotnet#create-incoming-webhooks-1). + +#### Message Content Options + +You can choose between two message content formats: + +- **Simple Message Content:** Supports plain text and basic markdown formatting. +- **Advanced Message Content:** Expects JSON input directly forwarded to Teams without modification. This format is + highly customizable and can be used for Adaptive Cards. + +Choose the format that best suits your messaging needs. + +### Silent Period + +The *Silent Period* is the duration, expressed in minutes, during which notifications are temporarily disabled after one +has been sent. This feature is implemented to prevent overwhelming the target with frequent notifications, avoiding +potential spam behavior. + +--- + +## Usage + +#### Simple Message Format + +In the simple message format, you can send plain text messages or utilize basic markdown formatting to convey +information. This is ideal for straightforward communication needs. + +#### Advanced Message Format + +For more sophisticated messaging requirements, the advanced message format allows you to send JSON content directly to +Microsoft Teams without modification. This feature is especially powerful when used +with [Adaptive Cards](https://learn.microsoft.com/en-us/adaptive-cards/), enabling interactive and dynamic content in +your Teams messages. diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.onesignal.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.onesignal.md new file mode 100644 index 000000000..0c4d6427d --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.onesignal.md @@ -0,0 +1,63 @@ +--- +id: org.apache.streampipes.sinks.notifications.jvm.onesignal +title: OneSignal +sidebar_label: OneSignal +--- + + + + + +

+ +

+ +*** + +## Description + +This sink sends a push message to the OneSignal application + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +Describe the configuration parameters here + +### App Id + +The OneSignal application ID. + +### API Key + +The OneSignal API key. + +### Content + +The message that should be sent to OneSignal + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.slack.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.slack.md new file mode 100644 index 000000000..7336200f6 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.slack.md @@ -0,0 +1,66 @@ +--- +id: org.apache.streampipes.sinks.notifications.jvm.slack +title: Slack Notification +sidebar_label: Slack Notification +--- + + + + + +

+ +

+ +*** + +## Description + +Slack bot to send notifications directly into your slack + +Before you use this sink, the Slack token needs to be configured. +After you've installed the element, navigate to ``Settings``, open the panel ``Sinks Notifications JVM`` and add your +Slack API token. +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +Describe the configuration parameters here + +### Receiver + +The receiver of the Slack message. + +### Channel Type + +The channel type, should be "User" or "Channel" + +### Content + +The message that should be sent. + +## Output + +(not applicable for data sinks) \ No newline at end of file diff --git a/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.telegram.md b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.telegram.md new file mode 100644 index 000000000..e00bb8f65 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/pe/org.apache.streampipes.sinks.notifications.jvm.telegram.md @@ -0,0 +1,70 @@ +--- +id: org.apache.streampipes.sinks.notifications.jvm.telegram +title: Telegram Publisher +sidebar_label: Telegram Publisher +--- + + + + + +

+ +

+ +*** + +## Description + +Publisher to send notifications to a Telegram channel. + +In order to be able to do so, you will have first to: +* Create a Telegram public [channel](https://telegram.org/tour/channels). +> Private channels/groups: also supported. +* Create a Telegram BOT via [@BotFather](https://core.telegram.org/bots#3-how-do-i-create-a-bot) and get an API key. +* Set the bot as [administrator](https://www.wikihow.com/Make-Someone-an-Admin-on-Telegram) in your channel. + +*** + +## Required input + +This sink does not have any requirements and works with any incoming event type. + +*** + +## Configuration + +Describe the configuration parameters here. + +### Bot API Key + +The API Key generated by `@BotFather` when you created your bot. + +### Channel Name or Chat Id + +The handle name of your public channel (e.g. `@channel_name`). +> For private channels/groups: handle name only available for public channels. Use `chat_id` instead. + +### Content + +The message to be sent. + +## Output + +(not applicable for data sinks) diff --git a/website-v2/versioned_docs/version-0.95.0/user-guide-first-steps.md b/website-v2/versioned_docs/version-0.95.0/user-guide-first-steps.md new file mode 100644 index 000000000..b7ca89f62 --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/user-guide-first-steps.md @@ -0,0 +1,209 @@ +--- +id: user-guide-first-steps +title: First steps +sidebar_label: First steps +--- + +In this section, you'll learn how to create and run pipelines in StreamPipes. +Before starting with this guide, all steps from the installation guide must be finished successfully and StreamPipes must be up and running. + +This guide consists of three steps: +First, you'll learn how to create your first pipeline using the interactive tutorial. +Afterwards, this section contains two examples on how to create pipelines based on the built-in data simulator. + +## Interactive Tutorial +The easiest way to get started with StreamPipes is the interactive tutorial. Once you open the pipeline editor for the first time, you'll see a splash screen as shown below. +Click **Start tour** to start the interactive tutorial, which will guide you through your first steps with StreamPipes. + +![Interactive Tutorial](/img/quickstart/interactive-tutorial.png) + +If the splash screen does not show up, make sure you've installed the following pipeline elements: +* Flow rate (data stream) +* Numerical Filter (processing element) +* Dashboard (data sink) + +After you've finished this very first tour, try the following tours to learn more about other features of StreamPipes: +* Open the Live Dashboard and start the dashboard tour (by clicking the school icon in the top menu bar) to learn how to create real-time visualizations +* Open StreamPipes Connect and start the tour to learn how to connect new data sources with StreamPipes. + +## First Examples + +In this tutorial, you will create two simple pipelines that demonstrate the basic functionality of StreamPipes. +The first example deals with monitoring a flow rate sensor. +This pipeline ensures that everything works properly and data is sent through the whole system. +In the second example we model a more complex situation detection pipeline that triggers a notification. +Let's start with our first pipeline. +If you have problems with any of the examples, please send us an email. +We are happy to help you. + +## Data Simulation +All the data sources that we have integrated in the StreamPipes demonstrator are simulated according to real world sensors. +For example a flow rate sensor in a water pipe, which measures how much water flows in that pipe or a water level sensor in a water tank, that reports how high the water level is in the tank. +In the next section you will use the flow rate sensor to build your first pipeline. + +## Flow Rate Visualization +In the first example, we create a live line chart to monitor the value of the mass flow from a flow rate sensor. + +### Create Pipeline +* As a first step go to the pipeline editor +* Select the **FRS1** (Flow Rate Source 1)source +* Then navigate to the **Data Sink** tab +* Select the **DS** (Dashboard Sink) and connect the source with the sink +* After connecting the elements save the pipeline by clicking on the save button on the top left corner +* In the save menu add a name *Flow Rate Monitoring* and a description *This is my very first pipeline!* +* Also select the **Start pipeline immediatly** checkbox +* Then click the button **Save and go to pipeline view** +* You are navigated to the pipeline view and a confirmation that the pipeline was started successfully should be shown + + + +### Create Visualization +* After we have created the pipeline we must create the line chart +* Navigate to the **Live Dashboard** of StreamPipes +* Click on the **Add visualization** button +* Select the just created pipeline **Flow Rate Monitoring** and click the **next** button +* For the visualization select the **Line Chart** and click **next** again +* Now you have to enter the configuration for the line chart + * Select time mapping: **timestamp** + * Select number mapping: **mass_flow** + * Range Minimum: **0** + * Range Maximum: **10** +* When all parameters are set correctly click the next button again. +* Congratulation you created the first pipeline and should now see the line chart + + + + +## Condition monitoring of a water tank +In our second example we are going to create a more complex pipeline. +This pipeline has two sources, the flow rate sensor from the previous example and a source measuring the water level in a tank. +Our goal is to send a notification when the flow rate stops and the water level in the water tank sinks too fast. +In this case a service technician should check the system as soon as possible. +This example should just illustrate how the individual components work. +Since the system currently uses simulated data each time the situation occurs a notification is triggered. + +Now lets start! + + +### Build the pipeline +* First we have to select the **FRS1** (Flow Rate Sensor 1) and **WL1** (Water Level) form the sources tab +* In a first step we want to detect when the flow rate stops +* Use the **NF** (Numerical Filter) from the processing elements tab and connect it to the **FRS1** source +* Configure the **Numerical Filter**: + * Field name for filter operator: **mass_flow** + * Filter Operation: **<** + * Threshold value: **1** + + + +* As a next step we add an aggregation to the water level. This reduces the inaccuracies we have because the water moves in the tank. +* Select the **A** (Aggregation) processing element +* Connect the **WL1** with **A** +* Configure **Aggregation**: + * Property Selection: **level** + * Operation: **Average** + * Time Window Size: **10** + * Output Frequency: **1** + * Groupe By: **None** + + + +* With the next processing element we check if the water level decreases too fast +* Select **I** (Trend) and connect it to **A** +* Configure **Trend**: + * Timestamp field: **timestamp** + * Value to observe: **aggregatedValue** (This value is created by previous element) + * Group by: **sensorId** + * Increase / Decrease: **Decrease** + * Percentage of Increase / Decrease: **20** + * Time Window Length: **10** + * Select Output: **All** + + + +* Now we connect the two stream with the sequence element, which checks if both events occur in a certain time +* Select **S** (Sequence) and connect both data streams to it +* Configure **Sequence**: + * Time Window Size: **1** + * Time Unit: **sec** + + + +* Now we create a notification for the service technician that something is wrong with the system +* Select **N** (Notification) from the data sink tab +* Connect **S** with **N** +* Configure **Notification**: + * Notification title: **Alarm** + * Content: **One notification was triggered by our first complex pipeline. Yeahhhh!** + + + +* Add the dashboard sink to the increase element to monitor the preliminary results +* Select **DS** and connect to **I** + + + +* Save the pipeline +* Save configuration: + * Pipeline Name: **Second Pipeline** + * Description: **Complex monitorung rule** + * Start pipeline immediately: **CHECK** +* Click **Save and go to pipeline view** +* All pipeline elements should be started successfully +* It can be seen that the elements run on different technologies, in flink and a java container + * http://pe-flink-examples:8090 + * http://pe-jvm-examples:8090 +* Go to visualization and create **Raw Data** visualization for the new pipeline + + + +* Every time you can see output in the **Raw Data** visualization of the new pipeline and the **Line Chart** from the first example are zero, a Notification is triggered by the pipeline. +* Go to the **Notifications** view and have a look at the notification + + + + +Congratulation you finished the quick start! +It is recommended to stop the last pipeline, because it will keep creating notifications ;) + +We hope we gave you an easy quick start into StreamPipes. +If you have any questions or suggestions, just send us an email. +From here on you can explore all features in the [User Guide](user-guide-introduction) or go to the [Developer Guide](extend-setup) to learn how to write your own StreamPipes processing elements. + diff --git a/website-v2/versioned_docs/version-0.95.0/user-guide-for-quickstart.md b/website-v2/versioned_docs/version-0.95.0/user-guide-for-quickstart.md new file mode 100644 index 000000000..ec4166a2f --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/user-guide-for-quickstart.md @@ -0,0 +1,134 @@ +--- +id: user-guide-for-quickstart +title: Quickstart Tour +sidebar_label: Quickstart Tour +--- + +Apache StreamPipes is a self-service Industrial IoT toolbox to enable non-technical users to connect, analyze and explore IoT data streams. + +Quickstart Mode, ideal for first-time users, allows users to interact with pre-set pipelines, dashboards, and data views to experience StreamPipes' functionalities for IIoT. +This page provides an overview of StreamPipes' features in Quickstart Mode, guiding users on how to utilize them effectively. + +:::info +Do you have StreamPipes already running in the quickstart mode? If not, simply run the command `docker-compose -f docker-compose.quickstart.yml build script-runner` and `docker-compose -f docker-compose.quickstart.yml up -d` from the directory `installer/compose`. +::: + +First of all, a user needs to log in to the system. The default login credentials are `admin@streampipes.apache.org` as username and `admin` as password. +The credentials for the user are specified during the installation process. + + +## Home +After logging in, the user is redirected on to the home page. +The home page gives an overview over the different features available in the StreamPipes UI. + +On the left, the navigation menu can be seen. +You can either use the icons on the left side or click on the menu icon on the +top left to open the details view of the navigation menu. + + + +## Connect +The _Connect_ view provides an overview of all existing adapters to let StreamPipes connect data sources. + +With StreamPipes Connect it is possible to connect new data sources in StreamPipes with just a few clicks. +It is also possible to connect specific data sources or connect generic sources like message brokers or databases. +If the event schema of the data source is unknown, the system tries to infer the schema by extracting some sample data and analysing it. + +In the Quickstart Mode, we use the Machine Data Simulator and Data Stream to simulate the factory machine condition data and environmental data, just click the `START ALL ADAPTERS` button. This starts all existing adapters to offer data sources for pipelines. + + + + +## Pipelines +The _Pipelines_ view provides an overview of all existing pipelines. + +A pipeline in Apache StreamPipes describes the transformation process from a data stream to a data sink. Typically, a pipeline consists of at least one data stream (or data set), zero or more data processors and at least one data sink. +Existing pipelines can be managed using this pipeline view. For instance, users can start and stop pipelines or delete them when they are not longer needed. + +In the pipeline page, we could see there are 5 pipelines, click the `START ALL ADAPTERS` button. This starts all existing adapters to offer data sources for pipelines. + + + + +In smart factory , monitoring and analysing machine parameters are essential steps to ensure machine safety, and minimizing downtime. In the industrial machine cooling system or hydraulic machines, water level could be an important part of monitoring parameters to ensure machine's performance and safety. +Using the `Water level trend analysis` pipeline as an example, by clicking the `Show pipeline` button, we could see the details of the pipeline as the following figure. + + + +The pipeline contains 3 major parts: +- **Data source**: + - Machine Data Simulator (Water Level). +- **Data Processors**: + - Trend Detector: to analyse the water level increase speed within specified time, when Trend Detector find the speed is reaching the limit, it will trigger the notification. + - Welford Change Detection: to calculate the variance of the mean and change of the water level over a specified time + - Boolean Counter: to count each `overflow` status change from `false` to `true`. + - Numerical Filter: when `overflow` status change from `false` to `true` reaches 5 times, it will trigger Notification. +- **Data Sinks**: + - Notification: to push a notification when the measured parameter condition reaches the set condition of the trigger, which are sent to Notification interface. + - Data Lake: to store the events in the internal data lake, offering data for dashboard and data explorer. + + +## Dashboard +The _Dashboard_ can be used for multiple use cases. + +It is a good way to live monitor a running system in a dashboard, it also could be used during the pipeline development to get a fast feedback from newly created pipelines. +Currently 10 different types of visualizations are available, including line charts, various map visualizations and data tables. The Dashboard feature allows you to select and view real-time data from specific pipelines that interest you. + +In the Quickstart mode, we offer three distinct dashboards, each designed for a specific application scenario. These dashboards include factory temperature monitoring, sensor running condition monitoring, and real-time machine data monitoring. + + + + +As shown in the following figure, taking the `Smart Machine Monitor` as an example, the dashboard can showcase real-time data for monitoring machine parameters such as pressure and water level. + + + +## Data Explorer +The _Data Explorer_ can be used for visualizing and exploring data streams that have been persisted using the Data Lake sink. + +Once your data has been stored in the data lake, you can navigate to the data explorer tab to craft a new data view with the widgets of your preference. +In StreamPipes, a data view is a compilation of related widgets, which can be data visualizations or plots, all assigned to a specific date and time range. By default, this range encompasses the last 15 minutes of the current date and time. However, you also have the flexibility to choose from predefined ranges, such as a day or a month, or to customize the exact date and time range that you wish to investigate. + +In the Quickstart Mode, we try to demonstrate the StreamPipes' practicability and convenience in data analysis, by providing example data views for specific application scenarios, including machine running condition monitor, problem analysis, factory environment monitor. + + + +As shown in the following figure, taking the `Machine Running Condition Monitoring of Water Level and Pressure` data view as an example, the data explorer can show real-time machine data as different types of widgets, depending on what the user needs, to help analyse the data better. +In the machine running condition scenarios, we pay attention to the distribution of water level and pressure, as well as the pressure change condition. + + + + + +## Notifications +The _Notifications_ can be used for notifying someone when a urgent situation occurs. + +A notification can be created by using the notification sink in the pipeline editor. +The message can be nicely configured in the message editor. +It is also possible to embed values of the event that triggered the notification. +All available properties are presented in the notification configurator. + +In Quickstart mode, StreamPipes have pre-defined several notification trigger in the example pipelines, when you click the `Notification` button in the upper right corner, you could see the notification dialog. + + + +Congratulations! You've just finished Quickstart user guide of StreamPipes. + diff --git a/website-v2/versioned_docs/version-0.95.0/user-guide-tour.md b/website-v2/versioned_docs/version-0.95.0/user-guide-tour.md new file mode 100644 index 000000000..a6ae2f8ff --- /dev/null +++ b/website-v2/versioned_docs/version-0.95.0/user-guide-tour.md @@ -0,0 +1,305 @@ +--- +id: user-guide-tour +title: Tour +sidebar_label: Tour +--- + +StreamPipes is a framework that enables domain experts to model and execute stream processing pipelines in a big data infrastructure. +The graphical user interface of StreamPipes is a web application that provides an easy to use solution for domain experts. +In this page, an overview of the many features of StreamPipes is given. We will tour through all features and explain what they do and how users can interact with them. + +First of all, a user needs to log in to the system. +The credentials for the user are specified during the installation process. + +![StreamPipes Login](/img/features_0_62_0/login.png) + +## Home +After logging in, the user is redirected on to the home page. +The home page gives an overview over the different features available in the StreamPipes UI. + +On the left, the navigation menu can be seen. +You can either use the icons on the left side or click on the menu icon on the +top left to open the details view of the navigation menu. + +On the top right, a link refers to the documentation and the logout button is present. + + + + + +## Pipeline Editor +The first feature we are going to explain is the Pipeline Editor. +This is one of the central features of StreamPipes, since graphical modeling of pipelines takes place in this view. + +On the top we can see four tabs: __Data Sets__, __Data Streams__, __Processing Elements__, and __Data Sinks__. +Each tab contains multiple pipeline elements, which can be installed at runtime. +The installation of pipeline elements is explained later in section [Install Pipeline Elements](user-guide-tour.md#install-pipeline-elements). + + + +There are multiple ways to search for a pipeline element. +The easiest way is to enter a search term in the search field on the top left corner. +The system filters the elements according to the search term and only presents the relevant ones. +Another way is to select one of the categories from the drop down menu. +The system then filters the elements according to the selected category. +The category of an element is defined by its functionality. + + + +Modelling of a pipeline starts with choosing one ore more data sources. Therefore a data stream or data set must be selected +and moved into the editor via drag and drop. +After adding a data stream, we can select a processing element to transform the events of the data stream. +This is done again by dragging the processing element icon into our editor. +The mouse can be used to connect the two elements. +It is automatically checked in the background if the two elements are semantically compatible. +If this check is successful, a configuration menu is shown. It contains the parameters that can be modified by the +user. For all parameters, a description is provided and the system also prevents the user from entering parameters that +are not correct, according to the semantic description of the element. +It is also ensured that all required parameters are provided by the user, otherwise an error message is displayed. + + + + +When the user tries to connect two elements that are not compatible, the system shows a __connection error__ message. +The example illustrated below shows such a case. The user tried to connect a text filter to a light sensor. +This is not possible since the text filter processing element requires at least one event property of type string, which is not provided by the light sensor. + +![Connection Error](/img/features_0_62_0/editor/10_connection_error.png) + +To further improve the usability, multiple ways are available to connect new elements besides the drag and drop option. +Each processing element has multiple short-cut buttons to connect it with another element. +The first one (eye icon) can be used to get a suggestion of all elements that are compatible with the current element. +The second one (plus icon) gives a recommendation on the elements the user might want to connect, based on the usage of the component in +other pipelines. +There is also a button (question tag icon) to open the documentation of a selected element. +Elements can be deleted by clicking the 'delete' button. +Each element can also be re-configured at a later point in time using the configuration button. +There is one important aspect about re-configuration of pipeline elements you need to be aware of: Only elements that are not yet connected to another element can be modified. +The reason for this behaviour is that some of the following elements might rely on the configuration of previous elements. +This way it is ensured that the user can not change the behaviour of the pipeline by accident. + + + + + +After the pipeline is completely modelled, the editor looks similar to the first image below. +Especially for larger pipelines, the auto layout button in the editor menu might be helpful. +With this button, the pipeline will be beautifully aligned in the editor, helping users to get a better overview of the complete pipeline. +On the top left corner of the editor, the 'save' button can be found. +After the modelling of the pipeline is done, use this button to save and execute the pipeline. +A save dialogue pops up when clicking the save button. +The pipeline title must be entered and an additional description can be provided. +It is recommended to always provide a description, because it makes it easier for other users to understand the meaning of the pipeline. +In the save menu, the user can either just store the pipeline configuration or store it and immediately start the pipeline. +Once the pipeline is executed, the user is redirected to the _Manage Pipeline_ view. +In this view, the user gets immediate feedback whether all components did start correctly and the pipeline is up and running. +This view also shows that individual elements might run in different environments on different servers. +If there is an error during the execution, a notification containing a (hopefully) detailed error description is provided in this view. + + +## Connect new Data Sources +With StreamPipes Connect it is possible to connect new data sources in StreamPipes with just a few clicks. +Therefore, we provide a marketplace with a lot of different adapters that can be configured and executed to create new __Data Streams__ in the __Pipeline Editor__. +With StreamPipes Connect it is possible to connect specific data sources - e.g. an adapter streaming the current location of the ISS (International Space Station). +It is also possible to connect generic sources like message brokers or databases. +If the event schema of the data source is unknown, the system tries to infer the schema by extracting some sample data and analysing it. + + + +Additionally to connecting new sources, data can be cleaned, transformed, and enriched with meta-information. +Therefore, the event schema can be changed or enriched in step 3 (Define Event Schema). + + + +## Manage Pipelines +The _Manage Pipelines_ view provides an overview of all existing pipelines. +Existing pipelines can be managed using this view. +For instance, users can start and stop pipelines or delete them when they are not longer needed. +Pipeline actions can be performed by clicking one of the buttons next to each pipeline. +For each pipeline, the title and description is displayed in the table. +By clicking the edit symbol an overview of the created pipline is shown. In this window you are able to analyze statistics, identify errors or edit the pipeline. + + + +In a setting with many defined pipelines, it can get really hard to keep track of all pipelines. +This is why we introduce categories. +A category is a set of pipelines that can be defined by users in order to better organize pipelines. +By clicking on the "Manage Categories" button on the top left, a new category can be added to StreamPipes. +In this example, we create a new category named "New Demo Category". +After saving a category, pipelines can be added to the newly created category. +The new category is then presented as a tab in the _Pipeline Management_ view. +This tab contains all previously defined pipelines. + + + +## Live Dashboard +The live dashboard can be used for multiple use cases. +It is a good way to live monitor a running system in a dashboard, but it can also be used during the pipeline development to get a fast feedback from newly created pipelines. +Below is a screenshot of an example dashboard showing the current value of pressure, which further shows a line chart and a trafficlight for a water level pipeline. +All pipelines that contain the "Dashboard Sink" can be visualized in this view. +To add a new visualisation, click on the "Add visualisation" button on the top left corner. +Afterwards, a three-step configuration menu is shown. +The first step is to select the pipeline that should be visualized. +In the second step, the type of visualization can be defined. +Currently 10 different types are available, including line charts, various map visualizations and data tables. +After selecting the type (in our example "Gauge"), you can select the specific measurement values of the data stream that should be displayed. +In the example below, the water level value should be monitored and the gauge value should range from 0 to 100. +Once all steps are completed, the new visualization is placed on the dashboard and live data is presented as soon as it becomes available. + + + +## File Download +With the file download, it is possible to download stored files directly from Elasticsearch. +This can be very useful for example when a data dumb is needed for a specific time range. +All data that is written into Elasticsearch using the _Elasticsearch Sink_ can be accessed by the file downloader. +A common use case is to download data for offline analysis and to train a machine learning algorithm. +First, an index must be defined, afterwards, the time range must be set. +A date picker helps users to enter the time range. +When a user clicks the "Create File" button the file is created. +All files stored on the server can be downloaded via the download button. +If the files are not longer needed, they can be deleted by clicking the delete button. +This will remove the file from the server. +Since data is stored in Elasticsearch anyways. it is recommended not to store the files for a longer period of time on the server. +When a file is needed again at a later point in time it is easy to create it again. +This way a lot of disk space on the server can be saved, especially when the files are rather large. + + + +## Notifications +Notifications are a good solution to notify someone when a urgent situation occurs. +A notification can be created by using the notification sink in the pipeline editor. +When using such a sink a configuration dialogue is presented to the user. +In this dialogue the user must provide enough information to resolve the solution when it occurs. +The message can be nicely configured in the message editor. +It is also possible to embed values of the event that triggered the notification. +This can be done with the #property# notation. +All available properties are presented in the notification configurator. +When the notification is triggered the #property# template is replaced with the actual value of the property. + + + +A pop up icon on the notification tab in the menu shows the user how many unread notifications currently are in the system. +This icon also alerts users when new notifications occur. +In the notification overview all notifications are listed. +On the top are the new notifications that are not read yet. +A user can mark them as read by clicking on the little envelope icon. +Those notifications are then no longer in the unread section, but they remain in the "All Messages" view. +This way it is possible to keep track of all notifications and have a look at them at a later point in time. + + + + +## Install Pipeline Elements +StreamPipes is highly configurable and extensible. +Pipeline elements can be added and removed during runtime. +This can be done in the "Install Pipeline Elements" view. +All available pipeline elements are shown here and can be selected to install or uninstall. +It is also possible to select multiple or all of them and then install them all together. +When a new element is installed by the user it is automatically available in the "Pipeline Editor" and can be used for pipelines. +Elements that are uninstalled are removed from the system. +They can not longer be used within pipelines. + + + + +## My Elements +The "My Elements" view gives a quick overview over all installed pipeline elements. +Here they can be inspected and the description can be re-imported. +In this view it is also possible to have a look at the JSON-LD representation of each element. +This is not important for a user, but it is worth noting that the system uses this machine understandable format to support the user. +For example all the information of the sources, like data schema or unit information is in the JSON-LD meta data. + + + +## Configuration +The last feature is the _configuration view_. +Here, it is possible to change the configuration parameters of installed components. +All components containing processing elements automatically register in StreamPipes when they are started in the same network as the backend component. +Once a container is registered it is represented in the configuration view. +The green dot on the top left indicates that tha container is running properly. +When there is a problem with a container the green dot changes to red and the user knows there is a problem. +To configure the parameters the top right arrow of a configuration box must be clicked. +Then the configuration menu pops up. +Within there it is possible to change the parameters of a service. +To make the changes persistent the "Update" button must be clicked. +A user should keep in mind that sometimes it is necessary to restart a container when the parameters are changed. +Sometimes it is also necessary to re-import the pipeline element description, either by uninstalling and re-installing them after the container restart or be reloading the description in the "My elements" view. + + + + +Congratulations! You've just finished your first tour of StreamPipes. +Although there's still more to learn, we introduced most of the currently available features. +On the next page, the different processing elements that come with the installation are explained. diff --git a/website-v2/versioned_sidebars/version-0.95.0-sidebars.json b/website-v2/versioned_sidebars/version-0.95.0-sidebars.json new file mode 100644 index 000000000..b3eba81d6 --- /dev/null +++ b/website-v2/versioned_sidebars/version-0.95.0-sidebars.json @@ -0,0 +1,203 @@ +{ + "documentation": { + "🚀 Try StreamPipes": [ + "user-guide-introduction", + "try-installation", + "user-guide-for-quickstart" + ], + "💡 Concepts": [ + "introduction", + "concepts-overview" + ], + "🎓 Use StreamPipes": [ + "use-connect", + "use-pipeline-editor", + "use-managing-pipelines", + "use-dashboard", + "use-data-explorer", + "use-notifications", + "use-install-pipeline-elements", + "use-configurations" + ], + "📚 Pipeline Elements": [ + { + "type": "category", + "label": "Adapters", + "items": [ + "pe/org.apache.streampipes.connect.iiot.protocol.stream.kafka", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.pulsar", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.rocketmq", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.tubemq", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.file", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.httpserver", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.http", + "pe/org.apache.streampipes.connect.adapters.iss", + "pe/org.apache.streampipes.connect.adapters.image.stream", + "pe/org.apache.streampipes.connect.iiot.adapters.influxdb.stream", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.mqtt", + "pe/org.apache.streampipes.connect.iiot.adapters.simulator.machine", + "pe/org.apache.streampipes.connect.iiot.protocol.stream.nats", + "pe/org.apache.streampipes.connect.iiot.adapters.netio.mqtt", + "pe/org.apache.streampipes.connect.iiot.adapters.netio.rest", + "pe/org.apache.streampipes.connect.iiot.adapters.oi4", + "pe/org.apache.streampipes.connect.iiot.adapters.opcua", + "pe/org.apache.streampipes.connect.iiot.adapters.plc4x.modbus", + "pe/org.apache.streampipes.connect.iiot.adapters.plc4x.s7", + "pe/org.apache.streampipes.connect.iiot.adapters.ros", + "pe/org.apache.streampipes.connect.iiot.adapters.iolink" + ] + }, + { + "type": "category", + "label": "Data Processors", + "items": [ + "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.counter", + "pe/org.apache.streampipes.processors.filters.jvm.processor.booleanfilter", + "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.inverter", + "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.logical", + "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timer", + "pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.state", + "pe/org.apache.streampipes.processors.transformation.jvm.csvmetadata", + "pe/org.apache.streampipes.processors.transformation.jvm.duration-value", + "pe/org.apache.streampipes.processors.textmining.jvm.chunker", + "pe/org.apache.streampipes.processors.filters.jvm.compose", + "pe/org.apache.streampipes.processors.transformation.jvm.count-array", + "pe/org.apache.streampipes.processors.siddhi.count", + "pe/org.apache.streampipes.processors.transformation.jvm.datetime", + "pe/org.apache.streampipes.processors.transformation.jvm.fieldhasher", + "pe/org.apache.streampipes.processors.transformation.jvm.field-mapper", + "pe/org.apache.streampipes.processors.transformation.jvm.fieldrename", + "pe/org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.buffergeometry", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.bufferpoint", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.reprojection", + "pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.revgeocoder.geocityname", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.latlngtojtspoint", + "pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversine", + "pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.distancecalculator.haversinestatic", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.epsg", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.complex", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.validation.simple", + "pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemaps", + "pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.geocoder.googlemapsstatic", + "pe/org.apache.streampipes.processors.geo.jvm.jts.processor.trajectory", + "pe/org.apache.streampipes.processors.geo.jvm.latlong.processor.speedcalculator", + "pe/org.apache.streampipes.processor.imageclassification.jvm.image-cropper", + "pe/org.apache.streampipes.processor.imageclassification.jvm.image-enricher", + "pe/org.apache.streampipes.processors.enricher.jvm.jseval", + "pe/org.apache.streampipes.processors.siddhi.listcollector", + "pe/org.apache.streampipes.processors.siddhi.listfilter", + "pe/org.apache.streampipes.processors.enricher.jvm.processor.math.mathop", + "pe/org.apache.streampipes.processors.transformation.jvm.booloperator.timekeeping", + "pe/org.apache.streampipes.processors.transformation.jvm.measurementunitconverter", + "pe/org.apache.streampipes.processors.filters.jvm.enrich", + "pe/org.apache.streampipes.processors.filters.jvm.schema", + "pe/org.apache.streampipes.processors.filters.jvm.movingaverage", + "pe/org.apache.streampipes.processors.textmining.jvm.namefinder", + "pe/org.apache.streampipes.processors.transformation.jvm.processor.state.labeler.number", + "pe/org.apache.streampipes.processors.transformation.jvm.round", + "pe/org.apache.streampipes.processors.filters.jvm.numericalfilter", + "pe/org.apache.streampipes.processors.siddhi.numericalfilter", + "pe/org.apache.streampipes.processors.filters.jvm.numericaltextfilter", + "pe/org.apache.streampipes.processors.textmining.jvm.partofspeech", + "pe/org.apache.streampipes.processors.filters.jvm.project", + "pe/org.apache.streampipes.processor.imageclassification.qrcode", + "pe/org.apache.streampipes.processors.filters.jvm.limit", + "pe/org.apache.streampipes.processors.textmining.jvm.sentencedetection", + "pe/org.apache.streampipes.processors.transformation.jvm.processor.booloperator.edge", + "pe/org.apache.streampipes.processors.transformation.jvm.split-array", + "pe/org.apache.streampipes.processors.enricher.jvm.processor.math.staticmathop", + "pe/org.apache.streampipes.processors.transformation.jvm.processor.staticmetadata", + "pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.counter", + "pe/org.apache.streampipes.processors.transformation.jvm.stringoperator.timer", + "pe/org.apache.streampipes.processors.transformation.jvm.processor.stringoperator.state", + "pe/org.apache.streampipes.processors.filters.jvm.sdt", + "pe/org.apache.streampipes.processors.filters.jvm.merge", + "pe/org.apache.streampipes.processors.transformation.jvm.taskduration", + "pe/org.apache.streampipes.processors.filters.jvm.textfilter", + "pe/org.apache.streampipes.processors.filters.jvm.threshold", + "pe/org.apache.streampipes.processors.filters.jvm.throughputmon", + "pe/org.apache.streampipes.processors.transformation.jvm.processor.timestampextractor", + "pe/org.apache.streampipes.processors.textmining.jvm.tokenizer", + "pe/org.apache.streampipes.processors.siddhi.topk", + "pe/org.apache.streampipes.processors.transformation.jvm.transform-to-boolean", + "pe/org.apache.streampipes.processors.siddhi.increase", + "pe/org.apache.streampipes.processors.enricher.jvm.processor.trigonometry", + "pe/org.apache.streampipes.processors.enricher.jvm.valuechange", + "pe/org.apache.streampipes.processors.transformation.jvm.changed-value", + "pe/org.apache.streampipes.processors.changedetection.jvm.welford" + ] + }, + { + "type": "category", + "label": "Data Sinks", + "items": [ + "pe/org.apache.streampipes.sinks.databases.jvm.iotdb", + "pe/org.apache.streampipes.sinks.brokers.jvm.bufferrest", + "pe/org.apache.streampipes.sinks.databases.jvm.couchdb", + "pe/org.apache.streampipes.sinks.internal.jvm.datalake", + "pe/org.apache.streampipes.sinks.databases.ditto", + "pe/org.apache.streampipes.sinks.notifications.jvm.email", + "pe/org.apache.streampipes.sinks.databases.jvm.influxdb", + "pe/org.apache.streampipes.sinks.brokers.jvm.jms", + "pe/org.apache.streampipes.sinks.brokers.jvm.kafka", + "pe/org.apache.streampipes.sinks.brokers.jvm.mqtt", + "pe/org.apache.streampipes.sinks.notifications.jvm.msteams", + "pe/org.apache.streampipes.sinks.brokers.jvm.nats", + "pe/org.apache.streampipes.sinks.internal.jvm.notification", + "pe/org.apache.streampipes.sinks.databases.jvm.opcua", + "pe/org.apache.streampipes.sinks.notifications.jvm.onesignal", + "pe/org.apache.streampipes.sinks.databases.jvm.postgresql", + "pe/org.apache.streampipes.sinks.brokers.jvm.pulsar", + "pe/org.apache.streampipes.sinks.brokers.jvm.rest", + "pe/org.apache.streampipes.sinks.brokers.jvm.rabbitmq", + "pe/org.apache.streampipes.sinks.databases.jvm.redis", + "pe/org.apache.streampipes.sinks.brokers.jvm.rocketmq", + "pe/org.apache.streampipes.sinks.notifications.jvm.slack", + "pe/org.apache.streampipes.sinks.notifications.jvm.telegram", + "pe/org.apache.streampipes.sinks.brokers.jvm.tubemq", + "pe/org.apache.streampipes.sinks.brokers.jvm.websocket" + ] + } + ], + "⚡ Deploy StreamPipes": [ + "choosing-the-right-flavor", + "deploy-docker", + "deploy-kubernetes", + "deploy-use-ssl", + "deploy-security", + "deploy-environment-variables" + ], + "💻 Customize StreamPipes": [ + "extend-setup", + "extend-cli", + "extend-archetypes", + "extend-first-processor", + "extend-tutorial-adapters", + "extend-tutorial-data-processors", + "extend-tutorial-data-sinks", + "extend-client", + "extend-sdk-functions", + "extend-sdk-event-model", + "extend-sdk-migration", + "extend-sdk-stream-requirements", + "extend-sdk-static-properties", + "extend-sdk-output-strategies", + "extend-customize-ui" + ], + "🔧 Technicals": [ + "technicals-architecture", + "technicals-runtime-wrappers", + "technicals-messaging" + ], + "👪 Community": [ + "community-get-help", + "community-contribute" + ] + }, + "faq": { + "FAQ": [ + "faq-common-problems" + ] + } +} diff --git a/website-v2/versions.json b/website-v2/versions.json index d4885ed5b..2e26bd1c4 100644 --- a/website-v2/versions.json +++ b/website-v2/versions.json @@ -1,4 +1,5 @@ [ + "0.95.0", "0.93.0", "0.92.0", "0.91.0",