You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2022/07/29 15:25:15 UTC
[plc4x] branch develop updated: feat(plc4go): first implementation of plc4xbrowser
This is an automated email from the ASF dual-hosted git repository.
sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/develop by this push:
new 027ef30a9 feat(plc4go): first implementation of plc4xbrowser
027ef30a9 is described below
commit 027ef30a90835d04f969dfc3161e440b271f42dd
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Jul 29 17:22:22 2022 +0200
feat(plc4go): first implementation of plc4xbrowser
This is a small terminal ui to communicate to a plc using plc4x
---
plc4go/go.mod | 6 +-
plc4go/go.sum | 16 +-
plc4go/tools/plc4xbrowser/main.go | 298 ++++++++++++++++++++++++++++++++++++++
3 files changed, 318 insertions(+), 2 deletions(-)
diff --git a/plc4go/go.mod b/plc4go/go.mod
index 6024c3228..e9399fef8 100644
--- a/plc4go/go.mod
+++ b/plc4go/go.mod
@@ -31,6 +31,7 @@ require (
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213
github.com/libp2p/go-reuseport v0.2.0
github.com/pkg/errors v0.9.1
+ github.com/rivo/tview v0.0.0-20220728094620-c6cff75ed57b
github.com/rs/zerolog v1.27.0
github.com/schollz/progressbar/v3 v3.8.7
github.com/snksoft/crc v1.1.0
@@ -45,8 +46,11 @@ require (
github.com/antchfx/xpath v0.0.0-20170515025933-1f3266e77307 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
+ github.com/gdamore/encoding v1.0.0 // indirect
+ github.com/gdamore/tcell/v2 v2.5.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
+ github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
@@ -56,7 +60,7 @@ require (
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/rivo/uniseg v0.2.0 // indirect
+ github.com/rivo/uniseg v0.3.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
diff --git a/plc4go/go.sum b/plc4go/go.sum
index b86f32bd2..e0883a5f6 100644
--- a/plc4go/go.sum
+++ b/plc4go/go.sum
@@ -68,6 +68,11 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
+github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
+github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
+github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04=
+github.com/gdamore/tcell/v2 v2.5.1 h1:zc3LPdpK184lBW7syF2a5C6MV827KmErk9jGVnmsl/I=
+github.com/gdamore/tcell/v2 v2.5.1/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -156,6 +161,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560=
github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k=
+github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
+github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -180,8 +187,11 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/tview v0.0.0-20220728094620-c6cff75ed57b h1:/RX/1JPloj+3P0aYh1N6jfKNWUg1NEIeAHFLc+8UOOU=
+github.com/rivo/tview v0.0.0-20220728094620-c6cff75ed57b/go.mod h1:/Ve2+D+tGMTMNAlGXKCIX9ZeX2InzODYHotmtKZUUVk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk=
+github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
@@ -361,16 +371,20 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/plc4go/tools/plc4xbrowser/main.go b/plc4go/tools/plc4xbrowser/main.go
new file mode 100644
index 000000000..d138cf2ec
--- /dev/null
+++ b/plc4go/tools/plc4xbrowser/main.go
@@ -0,0 +1,298 @@
+/*
+ * 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
+ *
+ * https://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.
+ */
+
+package main
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/gdamore/tcell/v2"
+ "github.com/pkg/errors"
+ "github.com/rivo/tview"
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+
+ "github.com/apache/plc4x/plc4go/internal/cbus"
+ plc4go "github.com/apache/plc4x/plc4go/pkg/api"
+ "github.com/apache/plc4x/plc4go/pkg/api/model"
+ "github.com/apache/plc4x/plc4go/pkg/api/transports"
+)
+
+// TODO: replace with real commands
+const plc4xCommands = "connect,disconnect,read,write,register,subscribe"
+
+var connections map[string]plc4go.PlcConnection
+var connectionsChanged func()
+
+var commandsExecuted int
+
+var messagesReceived int
+var messageOutput io.Writer
+
+func init() {
+ connections = make(map[string]plc4go.PlcConnection)
+}
+
+func main() {
+ application := tview.NewApplication()
+
+ newPrimitive := func(text string) tview.Primitive {
+ return tview.NewTextView().
+ SetTextAlign(tview.AlignCenter).
+ SetText(text)
+ }
+ connectionArea := buildConnectionArea(newPrimitive, application)
+ outputArea := buildOutputArea(newPrimitive, application)
+ commandArea := buildCommandArea(newPrimitive, application)
+
+ grid := tview.NewGrid().
+ SetRows(3, 0, 3).
+ SetColumns(30, 0, 30).
+ SetBorders(true).
+ AddItem(newPrimitive("PLC4X Browser"), 0, 0, 1, 3, 0, 0, false).
+ AddItem(newPrimitive("https://github.com/apache/plc4x"), 2, 0, 1, 3, 0, 0, false)
+
+ // Layout for screens narrower than 100 cells (connectionArea and side bar are hidden).
+ grid.AddItem(connectionArea, 0, 0, 0, 0, 0, 0, false).
+ AddItem(outputArea, 1, 0, 1, 3, 0, 0, false).
+ AddItem(commandArea, 0, 0, 0, 0, 0, 0, true)
+
+ // Layout for screens wider than 100 cells.
+ grid.AddItem(connectionArea, 1, 0, 1, 1, 0, 100, false).
+ AddItem(outputArea, 1, 1, 1, 1, 0, 100, false).
+ AddItem(commandArea, 1, 2, 1, 1, 0, 100, false)
+
+ if err := application.SetRoot(grid, true).EnableMouse(true).Run(); err != nil {
+ panic(err)
+ }
+ for _, connection := range connections {
+ connection.Close()
+ }
+}
+
+func buildConnectionArea(newPrimitive func(text string) tview.Primitive, application *tview.Application) tview.Primitive {
+ connectionAreaHeader := newPrimitive("Connections")
+ connectionArea := tview.NewGrid().
+ SetRows(3, 0).
+ SetColumns(0).
+ AddItem(connectionAreaHeader, 0, 0, 1, 1, 0, 0, false)
+ {
+ connectionList := tview.NewList()
+ connectionsChanged = func() {
+ application.QueueUpdateDraw(func() {
+ connectionList.Clear()
+ for connectionString, connection := range connections {
+ connectionList.AddItem(connectionString, "", 0x0, func() {
+ //TODO: disconnect popup
+ _ = connection
+ })
+ }
+ })
+ }
+ connectionArea.AddItem(connectionList, 1, 0, 1, 1, 0, 0, true)
+ }
+ return connectionArea
+}
+
+func buildCommandArea(newPrimitive func(text string) tview.Primitive, application *tview.Application) tview.Primitive {
+ commandAreaHeader := newPrimitive("Commands")
+ commandArea := tview.NewGrid().
+ SetRows(3, 0, 3).
+ SetColumns(0).
+ AddItem(commandAreaHeader, 0, 0, 1, 1, 0, 0, false)
+ {
+ enteredCommands := tview.NewTextView().
+ SetDynamicColors(true).
+ SetRegions(true).
+ SetWordWrap(true).
+ SetChangedFunc(func() {
+ application.Draw()
+ })
+ commandArea.AddItem(enteredCommands, 1, 0, 1, 1, 0, 0, false)
+
+ words := strings.Split(plc4xCommands, ",")
+ commandInputField := tview.NewInputField().
+ SetLabel("PLC4X Command").
+ SetFieldWidth(30)
+ commandInputField.
+ SetDoneFunc(func(key tcell.Key) {
+ commandText := commandInputField.GetText()
+ commandsExecuted++
+ _, _ = fmt.Fprintf(enteredCommands, "%s [\"%d\"]%s[\"\"]\n", time.Now().Format("04:05"), commandsExecuted, commandText)
+ go func() {
+ if err := handleCommand(commandText); err != nil {
+ _, _ = fmt.Fprintf(enteredCommands, "[#ff0000]%s %s[white]\n", time.Now().Format("04:05"), err)
+ return
+ }
+ application.QueueUpdateDraw(func() {
+ commandInputField.SetText("")
+ })
+ }()
+ })
+ commandInputField.SetAutocompleteFunc(func(currentText string) (entries []string) {
+ if len(currentText) == 0 {
+ return
+ }
+ for _, word := range words {
+ if strings.HasPrefix(strings.ToLower(word), strings.ToLower(currentText)) {
+ entries = append(entries, word)
+ }
+ }
+ switch {
+ case strings.HasPrefix(currentText, "subscribe"):
+ for connectionsString, _ := range connections {
+ entries = append(entries, "subscribe "+connectionsString)
+ }
+ }
+ if len(entries) <= 1 {
+ entries = nil
+ }
+ return
+ })
+ commandArea.AddItem(commandInputField, 2, 0, 1, 1, 0, 0, true)
+ }
+ return commandArea
+}
+
+func handleCommand(commandText string) error {
+ switch {
+ case strings.HasPrefix(commandText, "register "):
+ case strings.HasPrefix(commandText, "connect "):
+ host := strings.TrimPrefix(commandText, "connect ")
+ if _, ok := connections[host]; ok {
+ return errors.Errorf("%s already connected", host)
+ }
+ //TODO: we hardcode that to cbus for now
+ connectionString := fmt.Sprintf("c-bus://%s?srchk=true", host)
+ driverManager := plc4go.NewPlcDriverManager()
+ driverManager.RegisterDriver(cbus.NewDriver())
+ transports.RegisterTcpTransport(driverManager)
+ connectionResult := <-driverManager.GetConnection(connectionString)
+ if err := connectionResult.GetErr(); err != nil {
+ return errors.Wrapf(err, "%s can't connect to", host)
+ }
+ log.Info().Msgf("%s connected", host)
+ connections[host] = connectionResult.GetConnection()
+ connectionsChanged()
+ case strings.HasPrefix(commandText, "disconnect "):
+ host := strings.TrimPrefix(commandText, "disconnect ")
+ if connection, ok := connections[host]; !ok {
+ return errors.Errorf("%s not connected", host)
+ } else {
+ closeResult := <-connection.Close()
+ log.Info().Msgf("%s disconnected", host)
+ delete(connections, host)
+ connectionsChanged()
+ if err := closeResult.GetErr(); err != nil {
+ return errors.Wrapf(err, "%s can't close", host)
+ }
+ }
+ case strings.HasPrefix(commandText, "subscribe "):
+ host := strings.TrimPrefix(commandText, "subscribe ")
+ if connection, ok := connections[host]; !ok {
+ return errors.Errorf("%s not connected", host)
+ } else {
+ subscriptionRequest, err := connection.SubscriptionRequestBuilder().
+ AddEventQuery("something", "monitor/*/*").
+ AddItemHandler(func(event model.PlcSubscriptionEvent) {
+ messagesReceived++
+ _, _ = fmt.Fprintf(messageOutput, "[\"%d\"]\n%s[\"\"]", messagesReceived, event)
+ }).
+ Build()
+ if err != nil {
+ return errors.Wrapf(err, "%s can't subscribe", host)
+ }
+ subscriptionRequestResult := <-subscriptionRequest.Execute()
+ if err := subscriptionRequestResult.GetErr(); err != nil {
+ return errors.Wrapf(err, "%s can't subscribe", host)
+ }
+ log.Info().Msgf("subscription result %s", subscriptionRequestResult.GetResponse())
+ }
+ default:
+ return errors.Errorf("%s not found", commandText)
+ }
+ return nil
+}
+
+func buildOutputArea(newPrimitive func(text string) tview.Primitive, application *tview.Application) *tview.Grid {
+ outputAreaHeader := newPrimitive("Output")
+ outputArea := tview.NewGrid().
+ SetRows(3, 0, 10).
+ SetColumns(0).
+ AddItem(outputAreaHeader, 0, 0, 1, 1, 0, 0, false)
+ {
+ {
+ outputView := tview.NewTextView().
+ SetDynamicColors(true).
+ SetRegions(true).
+ SetWordWrap(true).
+ SetChangedFunc(func() {
+ application.Draw()
+ })
+ messageOutput = outputView
+
+ outputView.SetDoneFunc(func(key tcell.Key) {
+ currentSelection := outputView.GetHighlights()
+ if key == tcell.KeyEnter {
+ if len(currentSelection) > 0 {
+ outputView.Highlight()
+ } else {
+ outputView.Highlight("0").ScrollToHighlight()
+ }
+ } else if len(currentSelection) > 0 {
+ index, _ := strconv.Atoi(currentSelection[0])
+ if key == tcell.KeyTab {
+ index = (index + 1) % messagesReceived
+ } else if key == tcell.KeyBacktab {
+ index = (index - 1 + messagesReceived) % messagesReceived
+ } else {
+ return
+ }
+ outputView.Highlight(strconv.Itoa(index)).ScrollToHighlight()
+ }
+ })
+ outputView.SetBorder(false)
+ outputArea.AddItem(outputView, 1, 0, 1, 1, 0, 0, false)
+ }
+
+ {
+ consoleView := tview.NewTextView().
+ SetDynamicColors(true).
+ SetRegions(true).
+ SetWordWrap(true).
+ SetChangedFunc(func() {
+ application.Draw()
+ })
+
+ log.Logger = log.
+ //// Enable below if you want to see the filenames
+ //With().Caller().Logger().
+ Output(zerolog.ConsoleWriter{Out: tview.ANSIWriter(consoleView)}).
+ Level(zerolog.InfoLevel)
+
+ consoleView.SetBorder(false)
+ outputArea.AddItem(consoleView, 2, 0, 1, 1, 0, 0, false)
+ }
+ }
+ return outputArea
+}