summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwanglu <wang_lu@dl.cn.nexty-ele.com>2019-03-28 09:31:38 +0800
committerwanglu <wang_lu@dl.cn.nexty-ele.com>2019-04-12 09:25:29 +0800
commit39e4539925687d46c7bbe04681dcc09757dc9ae2 (patch)
tree05dea21f2f43b06b3ea260a4ddbb0b291392bcad
parent1e69db0b5eb9cfa6781e2c046d05dcca3a5bd909 (diff)
1.Add autobuild 2.Replace literal with notation in tsutils/tsrecorder/tsrecorder.c 3.Update LICENSE and COPYRIGHT This is a logging app with following functions included: 1.video recording 2.Audio recording 3.Can data recording & playing 4.Screen touch event recording & playing Change-Id: Id7942e16f87e69d25240f4985d3c47bc262d25c2 Signed-off-by: wanglu <wang_lu@dl.cn.nexty-ele.com>
-rw-r--r--.gitreview5
-rw-r--r--LICENSE204
-rw-r--r--app/app.pri12
-rw-r--r--app/app.pro22
-rw-r--r--app/images/HMI_Settings_DividingLine.svg58
-rw-r--r--app/images/HMI_Settings_X.svg72
-rw-r--r--app/images/images.qrc6
-rw-r--r--app/logfile/AudioLog.qml79
-rw-r--r--app/logfile/Bar.qml70
-rw-r--r--app/logfile/CANLog.qml29
-rw-r--r--app/logfile/LogFile.qml148
-rw-r--r--app/logfile/LogPlay.qml355
-rw-r--r--app/logfile/LogSave.qml238
-rw-r--r--app/logfile/TouchLog.qml29
-rw-r--r--app/logfile/TouchLogPlay.qml188
-rw-r--r--app/logfile/VideoLog.qml213
-rw-r--r--app/logfile/images/HMI_Settings_Button_Cancel.svg93
-rw-r--r--app/logfile/images/HMI_Settings_Button_Ok.svg93
-rw-r--r--app/logfile/images/HMI_Settings_LogFile.svg87
-rw-r--r--app/logfile/images/Keyboard_Arrow.svg65
-rw-r--r--app/logfile/images/Keyboard_Back.svg57
-rw-r--r--app/logfile/images/camerainfo_bg.svg93
-rw-r--r--app/logfile/keyboard/AbstractKeyboard.qml27
-rw-r--r--app/logfile/keyboard/Key.qml93
-rw-r--r--app/logfile/keyboard/Keyboard.qml40
-rw-r--r--app/logfile/keyboard/Numbers.qml130
-rw-r--r--app/logfile/logfile.qrc23
-rw-r--r--app/logfile/logplay.cpp290
-rw-r--r--app/logfile/logplay.h67
-rw-r--r--app/logfile/logsave.cpp360
-rw-r--r--app/logfile/logsave.h89
-rw-r--r--app/logfile/touchlogplay.cpp271
-rw-r--r--app/logfile/touchlogplay.h72
-rw-r--r--app/main.cpp131
-rwxr-xr-xautobuild/agl/autobuild58
-rw-r--r--autobuild/linux/autobuild62
-rw-r--r--log-utils.pro3
-rw-r--r--package/config.xml18
-rw-r--r--package/icon.svg283
-rw-r--r--package/package.pro21
-rw-r--r--tsutils/tsplayer/tsplayer.c332
-rw-r--r--tsutils/tsplayer/tsplayer.pro7
-rw-r--r--tsutils/tsrecorder/tsrecorder.c150
-rw-r--r--tsutils/tsrecorder/tsrecorder.pro7
-rw-r--r--tsutils/tsutils.c95
-rw-r--r--tsutils/tsutils.pro2
-rw-r--r--videoutils/videoutils.cpp255
-rw-r--r--videoutils/videoutils.h56
-rw-r--r--videoutils/videoutils.pro16
49 files changed, 5174 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..2285229
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.automotivelinux.org
+port=29418
+project=apps/settings-log-utils
+defaultbranch=master
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b3201ab
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,204 @@
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don`t include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/app/app.pri b/app/app.pri
new file mode 100644
index 0000000..014646f
--- /dev/null
+++ b/app/app.pri
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+load(configure)
+qtCompileTest(libhomescreen)
+
+config_libhomescreen {
+ CONFIG += link_pkgconfig
+ PKGCONFIG += homescreen
+ DEFINES += HAVE_LIBHOMESCREEN
+}
+
+DESTDIR = $${OUT_PWD}/../package/root/bin
diff --git a/app/app.pro b/app/app.pro
new file mode 100644
index 0000000..6f945bf
--- /dev/null
+++ b/app/app.pro
@@ -0,0 +1,22 @@
+TARGET = log-utils
+QT = quickcontrols2 websockets
+
+HEADERS += \
+ logfile/logsave.h \
+ logfile/logplay.h \
+ logfile/touchlogplay.h
+
+SOURCES = main.cpp \
+ logfile/logsave.cpp \
+ logfile/logplay.cpp \
+ logfile/touchlogplay.cpp
+
+CONFIG += link_pkgconfig
+PKGCONFIG += libhomescreen qlibwindowmanager qtappfw
+
+RESOURCES += \
+ images/images.qrc \
+ logfile/logfile.qrc
+
+
+include(app.pri)
diff --git a/app/images/HMI_Settings_DividingLine.svg b/app/images/HMI_Settings_DividingLine.svg
new file mode 100644
index 0000000..d63589c
--- /dev/null
+++ b/app/images/HMI_Settings_DividingLine.svg
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 932.8 1"
+ style="enable-background:new 0 0 932.8 1;"
+ xml:space="preserve"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="HMI_Settings_DividingLine.svg"><metadata
+ id="metadata18"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs16" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview14"
+ showgrid="false"
+ inkscape:zoom="0.72041167"
+ inkscape:cx="-116.6"
+ inkscape:cy="0.5"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><style
+ type="text/css"
+ id="style3">
+ .st0{opacity:0.302;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#A8A8A8;}
+</style><switch
+ id="switch5"><g
+ i:extraneous="self"
+ id="g7"><g
+ id="Divider_2_"
+ class="st0"><g
+ id="g10"><polygon
+ class="st1"
+ points="719.7,0 0,0 0,1 932.8,1 "
+ id="polygon12" /></g></g></g></switch></svg> \ No newline at end of file
diff --git a/app/images/HMI_Settings_X.svg b/app/images/HMI_Settings_X.svg
new file mode 100644
index 0000000..5ad9479
--- /dev/null
+++ b/app/images/HMI_Settings_X.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;#38;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 45 45"
+ style="enable-background:new 0 0 45 45;"
+ xml:space="preserve"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="HMI_ContactScreen_X-01.svg"><metadata
+ id="metadata66"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs64" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview62"
+ showgrid="false"
+ inkscape:zoom="5.2444444"
+ inkscape:cx="-132.61653"
+ inkscape:cy="22.5"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><style
+ type="text/css"
+ id="style4">
+ .st0{fill:none;stroke:#FFFFFF;stroke-miterlimit:10;}
+ .st1{display:none;}
+ .st2{display:inline;opacity:0.15;fill:url(#SVGID_1_);}
+ .st3{display:inline;opacity:0.35;fill:url(#SVGID_2_);}
+ .st4{display:inline;}
+ .st5{opacity:0.15;fill:url(#SVGID_3_);}
+ .st6{opacity:0.15;fill:url(#SVGID_4_);stroke:url(#SVGID_5_);stroke-miterlimit:10;}
+ .st7{fill:url(#SVGID_6_);}
+</style><switch
+ id="switch6"><g
+ i:extraneous="self"
+ id="g8"><g
+ id="Inactive"><g
+ id="g11"><line
+ class="st0"
+ x1="44.8"
+ y1="44.8"
+ x2="0.2"
+ y2="0.2"
+ id="line13" /><line
+ class="st0"
+ x1="45"
+ y1="0"
+ x2="0"
+ y2="45"
+ id="line15" /></g></g></g></switch></svg> \ No newline at end of file
diff --git a/app/images/images.qrc b/app/images/images.qrc
new file mode 100644
index 0000000..0bb2c0d
--- /dev/null
+++ b/app/images/images.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/images">
+ <file>HMI_Settings_DividingLine.svg</file>
+ <file>HMI_Settings_X.svg</file>
+ </qresource>
+</RCC>
diff --git a/app/logfile/AudioLog.qml b/app/logfile/AudioLog.qml
new file mode 100644
index 0000000..bb5d2b2
--- /dev/null
+++ b/app/logfile/AudioLog.qml
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+import '..'
+
+RowLayout {
+ id: audiolog
+
+ property int currentsps: 0
+ property int part1fontsize: 30
+ property int part2fontsize: 30
+ property int partheight: 50
+ property int part1width: 250
+ property int part2width: 600
+
+ Item {
+ implicitWidth: part1width
+ implicitHeight: partheight
+ Label {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ font.pixelSize: part1fontsize
+ text: 'SPS : '
+ }
+ }
+
+ TextField {
+ id: textfield
+ implicitWidth: part2width
+ implicitHeight: partheight
+ validator: IntValidator{bottom: 4000; top: 192000;}
+ color: 'white'
+ font.pixelSize: part2fontsize
+ verticalAlignment: TextInput.AlignBottom
+ Label {
+ x: parent.width - width
+ y: parent.height - height
+ text: '(4000~192000)'
+ color: 'gray'
+ font.pixelSize: 20
+ }
+ onTextChanged: {
+ if(text == '0') {
+ text = ''
+ }
+ if(text.length > 0) {
+ currentsps = text
+ }
+ else {
+ currentsps = 0
+ }
+ }
+ onFocusChanged: {
+ root.updateKeyBoard()
+ if(!focus && text < 4000) {
+ text = 4000
+ }
+ }
+ Component.onCompleted: {
+ root.addTarget(textfield)
+ }
+ }
+}
diff --git a/app/logfile/Bar.qml b/app/logfile/Bar.qml
new file mode 100644
index 0000000..3f0ac1f
--- /dev/null
+++ b/app/logfile/Bar.qml
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 2.0
+import '..'
+
+TabBar {
+ id: root
+ property int tabbtnwidth: 460
+ property int tabbtnheight: 51
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: 80
+ background.opacity: 0
+ Repeater {
+ id: tabs
+ model: ['Log Save', 'Log Play', 'Touch Log Play']
+ delegate: TabButton {
+ implicitWidth: tabbtnwidth
+ implicitHeight: tabbtnheight
+ contentItem: Text {
+ text: model.modelData
+ font.family: 'Roboto'
+ font.pixelSize: 30
+ opacity: enabled ? 1.0 : 0.3
+ color: parent.down ? "#17a81a" : "white"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+ background: Image {
+ anchors.fill: parent
+ transform: parent.down ? opacity = 1.0 : opacity = 0.75
+ }
+ }
+ }
+ Component.onCompleted: {
+ setBgImg()
+ }
+
+ onCurrentIndexChanged: {
+ setBgImg()
+ }
+
+ function setBgImg() {
+ for(var i = 0; i < tabs.count && i < root.count; i++) {
+ if(i == root.currentIndex) {
+ tabs.itemAt(i).background.source = './images/HMI_Settings_Button_Ok.svg';
+ }
+ else {
+ tabs.itemAt(i).background.source = './images/HMI_Settings_Button_Cancel.svg';
+ }
+ }
+ }
+}
diff --git a/app/logfile/CANLog.qml b/app/logfile/CANLog.qml
new file mode 100644
index 0000000..61cb7c5
--- /dev/null
+++ b/app/logfile/CANLog.qml
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+import '..'
+
+ColumnLayout {
+ anchors.left: parent.left
+ anchors.leftMargin: 100
+ Label {
+ text: 'There is no property which needs to be set.'
+ font.pixelSize: 30
+ }
+}
diff --git a/app/logfile/LogFile.qml b/app/logfile/LogFile.qml
new file mode 100644
index 0000000..da1dc40
--- /dev/null
+++ b/app/logfile/LogFile.qml
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 2.0
+import '..'
+import './keyboard'
+
+ApplicationWindow {
+ id: root
+ width: 1080 * screenInfo.scale_factor()
+ height: 1487 * screenInfo.scale_factor()
+// icon: '/logfile/images/HMI_Settings_LogFile.svg'
+ title: 'LogFile'
+ property string trippath: ''
+ property string rootpath: ''
+ property var targets: new Array()
+
+ Bar {
+ id: tabbar
+ anchors.top: parent.top
+ anchors.topMargin: 200
+ onCurrentIndexChanged: {
+ keyboard.visible = false
+ }
+ }
+
+ StackLayout {
+ id: pages
+ anchors.left: tabbar.left
+ anchors.right: tabbar.right
+ anchors.top: tabbar.bottom
+ currentIndex: tabbar.currentIndex
+ LogSave { id: logsave }
+ LogPlay { id: logplay }
+ TouchLogPlay { id: touchlogplay }
+ }
+
+ Keyboard {
+ id: keyboard
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ active: numbers
+ onVisibleChanged: {
+ var children = pages.children
+ for(var child in children) {
+ if(children[child].visible) {
+ children[child].resetPosition()
+ break;
+ }
+ }
+ }
+ Numbers {
+ id: numbers
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.leftMargin: parent.height / 2
+ anchors.rightMargin: parent.height / 2
+ target: parent.target
+ onHide: {
+ keyboard.visible = false
+ target.focus = false
+ }
+ onNext: {
+ var nexttarget
+ for(var i = 0; i < targets.length; i++) {
+ if(targets[i] === target) {
+ for(var j = i, k = 0; k < targets.length; k++) {
+ if(j === targets.length-1) {
+ j = 0;
+ }
+ else {
+ j++;
+ }
+ if(targets[j].visible) {
+ target.focus = false
+ nexttarget = targets[j]
+ nexttarget.focus = true
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ onVisibleChanged: {
+ if(!visible) {
+ keyboard.visible = false
+ }
+ }
+
+ function updateKeyBoard() {
+ var textfield = 0
+ for(var i = 0; i < targets.length; i++) {
+ if(targets[i].focus) {
+ textfield = targets[i]
+ }
+ }
+
+ if(textfield === 0) {
+ keyboard.visible = false
+ return
+ }
+ else {
+ keyboard.target = textfield
+ keyboard.visible = true
+ }
+
+ var implicity = 0.0
+ for(var itext = textfield; itext !== pages; itext = itext.parent) {
+ implicity += itext.y
+ }
+ implicity += itext.y
+
+ var keyboardy = implicity + textfield.height
+ if(keyboard.y < keyboardy) {
+ var children = pages.children
+ for(var child in children) {
+ if(children[child].visible) {
+ children[child].adjustPosition(keyboardy - keyboard.y)
+ break;
+ }
+ }
+ }
+ }
+
+ function addTarget(target) {
+ targets.push(target)
+ }
+}
diff --git a/app/logfile/LogPlay.qml b/app/logfile/LogPlay.qml
new file mode 100644
index 0000000..c8e68fd
--- /dev/null
+++ b/app/logfile/LogPlay.qml
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+import LogPlayImpl 1.0
+import '..'
+
+ColumnLayout {
+ property int blankheight : 50
+ property bool playing: false
+
+ property var timelist : ''
+ property var stime : ['1970', '01', '01', '00', '00', '00']
+ property var etime : ['1970', '01', '01', '00', '00', '00']
+ property var portslist : ['None']
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ Item { height:blankheight }
+
+ RowLayout{
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Label { text: 'CAN Log files: ' ; color: '#59FF7F'}
+ RowLayout{
+ anchors.right: parent.right
+ Label { text: 'Port : ' }
+ ComboBox {
+ id: ports
+ implicitWidth: 220
+ font.family: 'Roboto'
+ font.pixelSize: 30
+ model: portslist
+ contentItem: Text {
+ text: ports.displayText
+ font: ports.font
+ color: ports.pressed ? "#17a81a" : "white"
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+ indicator: Canvas {
+ id: canvas
+ x: ports.width - width - ports.rightPadding
+ y: ports.topPadding + (ports.availableHeight - height) / 2
+ width: 20
+ height: 12
+ contextType: "2d"
+
+ Connections {
+ target: ports
+ onPressedChanged: canvas.requestPaint()
+ }
+
+ onPaint: {
+ context.reset();
+ context.moveTo(0, 0);
+ context.lineTo(width, 0);
+ context.lineTo(width / 2, height);
+ context.closePath();
+ context.fillStyle = ports.pressed ? "#17a81a" : "white";
+ context.fill();
+ }
+ }
+ popup: Popup {
+ id: portspopup
+ y: ports.height - 1
+ implicitWidth: ports.width
+ implicitHeight: listview.contentHeight
+ padding: 0
+
+ contentItem: ListView {
+ id: listview
+ clip: true
+ model: portspopup.visible ? ports.delegateModel : null
+ currentIndex: ports.highlightedIndex
+ ScrollIndicator.vertical: ScrollIndicator { }
+ }
+
+ background: Image {
+ source:'./images/HMI_Settings_Button_Cancel.svg'
+ }
+ }
+ delegate: ItemDelegate {
+ id: popupdelegate
+ width: ports.width
+ contentItem: Item {
+ implicitHeight: 30
+ Text {
+ text: modelData
+ color: popupdelegate.pressed || highlighted ? "#21be2b" : "white"
+ font: ports.font
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ highlighted: ports.highlightedIndex == index
+ }
+
+ background: Image {
+ source:'./images/HMI_Settings_Button_Cancel.svg'
+ }
+ onCurrentTextChanged: {
+ if(currentText != 'None') {
+ timelist = logplayimpl.getCanLogTime(currentText)
+ }
+ }
+ onModelChanged: {
+ if(currentText == '' || currentText == 'None') {
+ enabled = false
+ }
+ else {
+ enabled = true
+ }
+ }
+ }
+ }
+ }
+ Item { height:blankheight/3 }
+ Image { source: '../images/HMI_Settings_DividingLine.svg' }
+ Item { height:blankheight/3 }
+ ListView {
+ id: loglist
+ property int impheight: 0
+ property int listitemheight : 50
+ property int listmaxheight : 600
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: 20
+ implicitHeight: 0
+ clip: true
+ model:logplayimpl
+ delegate: MouseArea {
+ height: loglist.listitemheight
+ width: ListView.view.width
+ Label {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ visible: model.children > 0 ? true : false
+ text: '' + model.children
+ color: 'gray'
+ font.pixelSize: 30
+ }
+ Image {
+ source: '../images/HMI_Settings_DividingLine.svg'
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ visible: model.index > 0
+ }
+ Image {
+ visible: loglist.currentIndex === model.index ? true : false
+ anchors.fill: parent
+ source:'./images/HMI_Settings_Button_Cancel.svg'
+ }
+ Label {
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 5
+ text: model.folder ? model.name : ' > ' + model.name
+ color: model.folder ? 'gray' : (loglist.currentIndex === model.index ? 'orange' : 'white')
+ font.pixelSize: 30
+ }
+ onClicked: {
+ if(!model.folder) {
+ loglist.currentIndex = model.index
+ }
+ }
+ }
+ onCountChanged: {
+ impheight = (count * listitemheight) > listmaxheight ? listmaxheight : (count * listitemheight)
+ implicitHeight = impheight
+ }
+
+ Component.onCompleted: {
+ currentIndex = -1
+ }
+ onCurrentIndexChanged: {
+ model.setLogFileIndex(currentIndex)
+ portslist = model.getPortsList()
+ if(portslist.length == 0) {
+ portslist = ['None']
+ }
+ timelist = model.getCanLogTime(ports.currentText)
+ }
+ }
+ Item { height:blankheight/3 }
+ Image { source: '../images/HMI_Settings_DividingLine.svg' }
+ Item { height:blankheight/3 }
+ Repeater {
+ id: logtime
+ model: ['Start Time : ', 'End Time : ']
+ delegate: RowLayout {
+ property var curidx: model.index
+ Item {
+ implicitHeight: 50
+ implicitWidth: 250
+ Label {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ text: model.modelData
+ }
+ }
+ Repeater{
+ id: datetime
+ property list<IntValidator> range: [
+ IntValidator{bottom: 0; top: 23;},
+ IntValidator{bottom: 0; top: 59;},
+ IntValidator{bottom: 0; top: 59;}
+ ]
+ model: 6
+ RowLayout {
+ implicitHeight: 50
+ implicitWidth: 120
+ Label {
+ anchors.bottom: parent.bottom
+ visible: model.index > 0 ? true : false
+ text: model.index === 3 ? ' ' : model.index > 3 ? ':' : '/'
+ color: model.index < 3 ? 'gray' : 'white'
+ font.pixelSize: 30
+ }
+ TextField {
+ implicitWidth: model.index > 0 ? 60 : 90
+ width: model.index > 0 ? 60 : 90
+ text: (curidx*datetime.count + model.index) >= timelist.length ? '-' : timelist[(curidx*datetime.count + model.index)]
+ color: model.index > 2 ? 'white' : 'gray'
+ enabled: false
+ font.pixelSize: 30
+ horizontalAlignment: TextInput.AlignHCenter
+ verticalAlignment: TextInput.AlignBottom
+ onTextChanged: {
+ if(model.index > 2 && text !== '/') {
+ enabled = true
+ if(text.length > 2) {
+ text = text.substring(text.length-2, text.length)
+ }
+ }
+ else {
+ enabled = false
+ }
+ if(curidx == 0 ) {
+ if(model.index < stime.length) {
+ stime[model.index] = text;
+ }
+ }
+ else {
+ if(model.index < stime.length) {
+ etime[model.index] = text;
+ }
+ }
+ }
+ onFocusChanged: {
+ root.updateKeyBoard()
+ if(!focus) {
+ if(text.length === 0) {
+ text = '00'
+ }
+ else if(text.length === 1 && text !== '/') {
+ text = '0' + text
+ }
+ }
+ }
+ }
+ }
+ Component.onCompleted: {
+ for(var i = 3, j = 0; i < count && j < range.length; i++, j++) {
+ itemAt(i).children[1].validator = range[j]
+ root.addTarget(itemAt(i).children[1])
+ }
+ }
+ }
+ Label {
+ property int timeindex: 3+curidx*datetime.count
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ text: (timelist.length <= timeindex+2) ? '(-:-:-)' :
+ '('+timelist[timeindex]+':'+timelist[timeindex+1]+':'+timelist[timeindex+2]+')'
+ color: 'gray'
+ font.pixelSize: 20
+ }
+ }
+ }
+ Item { height:blankheight/3 }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Button {
+ anchors.left: parent.left
+ anchors.leftMargin: 100
+ text: 'Refresh'
+ highlighted: true
+ onClicked: {
+ loglist.currentIndex = -1
+ logplayimpl.refresh()
+ }
+ }
+ Button {
+ anchors.right: parent.right
+ anchors.rightMargin: 100
+ text: playing ? 'Stop' : 'Play'
+ highlighted: true
+ onClicked: {
+ if(playing) {
+ playing = false
+ logplayimpl.CANPlay(false)
+ }
+ else {
+ if(logplayimpl.checkTime(ports.currentText, stime, etime))
+ {
+ logplayimpl.setCANProperty(loglist.currentIndex, ports.currentText, stime, etime)
+ if(logplayimpl.CANPlay(true))
+ playing = true
+ }
+ }
+ }
+ }
+ }
+
+ LogPlayImpl { id: logplayimpl }
+
+ onVisibleChanged: {
+ if(visible) {
+ logplayimpl.refresh()
+ }
+ else {
+ resetPosition()
+ }
+ }
+
+ function adjustPosition(offset) {
+ loglist.implicitHeight -= offset
+ loglist.positionViewAtIndex(loglist.currentIndex, ListView.Beginning)
+ }
+
+ function resetPosition() {
+ loglist.implicitHeight = loglist.impheight
+ }
+}
diff --git a/app/logfile/LogSave.qml b/app/logfile/LogSave.qml
new file mode 100644
index 0000000..3b8a6ad
--- /dev/null
+++ b/app/logfile/LogSave.qml
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 2.0
+import LogSaveImpl 1.0
+import '..'
+
+ColumnLayout {
+ property int blankheight : 50
+ property bool saving : false
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ Item { height:blankheight }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Label {
+ text: 'CurPath : '
+ color: 'white'
+ }
+ Label {
+ id: logpath
+ text: ''
+ color: 'white'
+ font.italic: true
+ font.pixelSize: 20
+ }
+ }
+
+ // Can log
+ Item { height:blankheight/3 }
+ Image {
+ width: tabbar.width
+ source: '../images/HMI_Settings_DividingLine.svg'
+ }
+ Item { height:blankheight/3 }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Label { text: 'CAN'; color: '#59FF7F' }
+ Switch { id: canswitch;anchors.right: parent.right }
+ }
+// Item { height:blankheight/3 }
+ CANLog {}
+
+ // Touch log
+ Item { height:blankheight/3 }
+ Image {
+ width: tabbar.width
+ source: '../images/HMI_Settings_DividingLine.svg'
+ }
+ Item { height:blankheight/3 }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Label { text: 'Touch'; color: '#59FF7F' }
+ Switch { id: touchswitch;anchors.right: parent.right }
+ }
+// Item { height:blankheight/3 }
+ TouchLog {}
+
+ // Video log
+ Item { height:blankheight/3 }
+ Image {
+ width: tabbar.width
+ source: '../images/HMI_Settings_DividingLine.svg'
+ }
+ Item { height:blankheight/3 }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Label { text: 'Video'; color: '#59FF7F' }
+ Switch { id: videoswitch;anchors.right: parent.right }
+ }
+// Item { height:blankheight/3 }
+ VideoLog { id: videolog }
+
+ //Audio log
+ Item { height:blankheight/3 }
+ Image {
+ width: tabbar.width
+ source: '../images/HMI_Settings_DividingLine.svg'
+ }
+ Item { height:blankheight/3 }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Label { text: 'Audio'; color: '#59FF7F' }
+ Switch { id: audioswitch;anchors.right: parent.right }
+ }
+// Item { height:blankheight/3 }
+ AudioLog { id: audiolog }
+
+ //Status bar
+ Item { height:blankheight/3 }
+ Image {
+ width: tabbar.width
+ source: '../images/HMI_Settings_DividingLine.svg'
+ }
+ Item { height:blankheight/3 }
+ RowLayout {
+ Label {
+ text: 'Status: '
+ color: '#59FF7F'
+ }
+ Label {
+ id: recordstatus
+ property int hour: 0
+ property int minute: 0
+ property int second: 0
+ property string duration: ''
+ text: saving ? ('Log Saving... '+ duration) : (hour > 0 || minute > 0 || second > 0) ? ('Duration: ' + duration) : 'Press SaveStart to save log file.'
+ font.pixelSize: 30
+ Timer {
+ id: timer
+ property double seconds: 0
+ interval: 1000
+ running: false
+ repeat: true
+ onTriggered: {
+ if(saving) {
+ seconds++
+ parent.hour = seconds/(60*60)
+ parent.minute = parent.hour > 0 ? seconds%(60*60)/60 : seconds/60
+ parent.second = seconds - parent.hour*(60*60) - parent.minute*60
+ parent.duration = (parent.hour > 0 ? (parent.hour + 'h ') : '')+(parent.minute > 0 ? (parent.minute + 'm ') : '')+(parent.second > 0 ? (parent.second + 's') : '')
+ }
+ }
+ }
+ }
+ }
+
+ // Buttons
+ Item { height:blankheight/3 }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Button {
+ id: savestart
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: saving ? 'SaveStop' : 'SaveStart'
+ highlighted: true
+ onClicked: {
+ if(!saving && (canswitch.checked || touchswitch.checked || videoswitch.checked || audioswitch.checked)) {
+ if(logsaveimpl.createLogFolders()) {
+ logpath.text = logsaveimpl.getCurrentSeg()
+
+ if(canswitch.checked) {
+ logsaveimpl.setCANProperty()
+ }
+
+ if(touchswitch.checked) {
+ logsaveimpl.setTouchProperty()
+ }
+
+ if(videoswitch.checked) {
+ logsaveimpl.setVideoProperty(videolog.properties)
+ }
+
+ if(audioswitch.checked) {
+ logsaveimpl.setAudioProperty(audiolog.currentsps)
+ }
+
+ console.log("touchswitch.checked: " + touchswitch.checked)
+ if(logsaveimpl.saveStart(canswitch.checked, touchswitch.checked, videoswitch.checked, audioswitch.checked)) {
+ recordstatus.hour = recordstatus.minute = recordstatus.second = 0
+ recordstatus.duration = ''
+ timer.seconds = 0
+ timer.start()
+ saving = true
+ }
+ }
+ }else if(saving) {
+ logsaveimpl.saveStop()
+ saving = false
+ timer.stop()
+ }
+ }
+ }
+ }
+ LogSaveImpl {
+ id: logsaveimpl
+ }
+ Component.onCompleted: {
+ if(logsaveimpl.createTripFolder()) {
+ logpath.text = trippath = logsaveimpl.getCurrentTrip()
+ }
+ else {
+ logpath.text = ""
+ }
+ }
+
+ onVisibleChanged: {
+ if(!visible) {
+ resetPosition()
+ }
+ }
+
+ function adjustPosition(offset) {
+ y -= offset
+ var heights = 0
+ for(var child in children) {
+ heights += children[child].height
+ if(heights <= offset) {
+ children[child].opacity = 0
+ }
+ else {
+ children[child].opacity = 0
+ break
+ }
+ }
+ }
+
+ function resetPosition() {
+ var children = logsave.children
+ for(var child in children) {
+ children[child].opacity = 1
+ }
+ logsave.y = 0
+ }
+}
diff --git a/app/logfile/TouchLog.qml b/app/logfile/TouchLog.qml
new file mode 100644
index 0000000..61cb7c5
--- /dev/null
+++ b/app/logfile/TouchLog.qml
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+import '..'
+
+ColumnLayout {
+ anchors.left: parent.left
+ anchors.leftMargin: 100
+ Label {
+ text: 'There is no property which needs to be set.'
+ font.pixelSize: 30
+ }
+}
diff --git a/app/logfile/TouchLogPlay.qml b/app/logfile/TouchLogPlay.qml
new file mode 100644
index 0000000..3d78b95
--- /dev/null
+++ b/app/logfile/TouchLogPlay.qml
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+import TouchLogPlayImpl 1.0
+import '..'
+
+ColumnLayout {
+ property int blankheight : 50
+// property bool playing: false
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ Item { height:blankheight }
+
+ // Title
+ RowLayout{
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Label { text: 'Touch Log files: ' ; color: '#59FF7F'}
+ }
+
+ Item { height:blankheight/3 }
+ Image { source: '../images/HMI_Settings_DividingLine.svg' }
+ Item { height:blankheight/3 }
+
+ // List view
+ ListView {
+ id: touchloglist
+ property int impheight: 0
+ property int listitemheight : 50
+ property int listmaxheight : 600
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: 20
+ implicitHeight: 0
+ clip: true
+ model:touchlogplayimpl
+ delegate: MouseArea {
+ height: touchloglist.listitemheight
+ width: ListView.view.width
+ Label {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ visible: model.children > 0 ? true : false
+ text: '' + model.children
+ color: 'gray'
+ font.pixelSize: 30
+ }
+ Image {
+ source: '../images/HMI_Settings_DividingLine.svg'
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ visible: model.index > 0
+ }
+ Image {
+ visible: touchloglist.currentIndex === model.index ? true : false
+ anchors.fill: parent
+ source:'./images/HMI_Settings_Button_Cancel.svg'
+ }
+ Label {
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 5
+ text: model.folder ? model.name : ' > ' + model.name
+ color: model.folder ? 'gray' : (touchloglist.currentIndex === model.index ? 'orange' : 'white')
+ font.pixelSize: 30
+ }
+ onClicked: {
+ if(!model.folder) {
+ touchloglist.currentIndex = model.index
+ }
+ }
+ }
+ onCountChanged: {
+ impheight = (count * listitemheight) > listmaxheight ? listmaxheight : (count * listitemheight)
+ implicitHeight = impheight
+ }
+
+ Component.onCompleted: {
+ currentIndex = -1
+ }
+ onCurrentIndexChanged: {
+ model.setLogFileIndex(currentIndex)
+ }
+ }
+
+ Item { height:blankheight/3 }
+ Image { source: '../images/HMI_Settings_DividingLine.svg' }
+ Item { height:blankheight/3 }
+
+ Item { height:blankheight/3 }
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Button {
+ anchors.left: parent.left
+ anchors.leftMargin: 100
+ text: 'Refresh'
+ highlighted: true
+ onClicked: {
+ touchloglist.currentIndex = -1
+ touchlogplayimpl.refresh()
+ }
+ }
+ Button {
+ id:playstopbutton
+ anchors.right: parent.right
+ anchors.rightMargin: 100
+ text: touchlogplayimpl.propTouchPlayFlag ? 'Stop' : 'Play'
+ highlighted: true
+
+ onClicked: {
+ console.log("status:", touchlogplayimpl.propTouchPlayFlag);
+ if(touchlogplayimpl.propTouchPlayFlag) {
+ touchlogplayimpl.touchPlay(false)
+ }
+ else {
+ touchlogplayimpl.setTouchProperty(touchloglist.currentIndex)
+ touchlogplayimpl.touchPlay(true)
+ }
+ }
+ }
+ }
+
+ Timer {
+ id:countDown
+ repeat: false
+ triggeredOnStart: true
+ onTriggered: {
+ console.log("countDown onTriggered:");
+ playstopbutton.text = 'Play'
+ countDown.stop();
+ }
+ }
+
+ TouchLogPlayImpl {
+ id: touchlogplayimpl
+
+ onPropTouchPlayFlagChanged: {
+ console.log("onPropTouchPlayFlagChanged:", touchlogplayimpl.propTouchPlayFlag);
+ if(touchlogplayimpl.propTouchPlayFlag) {
+ playstopbutton.text = 'Stop'
+ console.log("Stop:");
+ } else {
+ //playstopbutton.text = 'Play'
+ console.log("countDown.start():");
+ countDown.start();
+ console.log("countDown.started():");
+ }
+ }
+ }
+
+ onVisibleChanged: {
+ if(visible) {
+ touchlogplayimpl.refresh()
+ }
+ else {
+ resetPosition()
+ }
+ }
+
+ function adjustPosition(offset) {
+ touchloglist.implicitHeight -= offset
+ touchloglist.positionViewAtIndex(touchloglist.currentIndex, ListView.Beginning)
+ }
+
+ function resetPosition() {
+ touchloglist.implicitHeight = touchloglist.impheight
+ }
+}
diff --git a/app/logfile/VideoLog.qml b/app/logfile/VideoLog.qml
new file mode 100644
index 0000000..325bea8
--- /dev/null
+++ b/app/logfile/VideoLog.qml
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+import '..'
+
+RowLayout {
+ id: videolog
+
+ property var properties: ['', 0, 0,0]
+
+ property int part1width: 250
+ property int part2width: 600
+ property int partheight: 50
+ property int part1fontsize: 30
+ property int part2fontsize: 30
+ property var cameradevs: []
+
+ ColumnLayout{
+ Repeater {
+ model: ['Zone : ', 'FPS : ', 'Width : ', 'Height : ']
+ delegate: Item {
+ implicitWidth: part1width
+ implicitHeight: partheight
+ Label {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ font.pixelSize: part1fontsize
+ text: model.modelData
+ }
+ }
+ }
+ }
+ ColumnLayout{
+// RowLayout{
+// implicitWidth: part2width
+// implicitHeight: partheight
+// ButtonGroup {
+// id: zonegroup
+// }
+// Repeater {
+// id: zones
+// model: ['None', 'Front', 'Rear', 'Left', 'Right']
+// delegate: CheckBox {
+// text: model.modelData
+// font.family: 'Roboto'
+// font.pixelSize: part2fontsize
+// ButtonGroup.group: zonegroup
+// contentItem: Text {
+// text: parent.text
+// font: parent.font
+// color:'white'
+// horizontalAlignment: Text.AlignHCenter
+// verticalAlignment: Text.AlignVCenter
+// leftPadding: parent.indicator.width + parent.spacing
+// }
+// onCheckedChanged: {
+// if(checked) {
+// properties[0] = text
+// }
+// }
+// }
+// Component.onCompleted: {
+// zones.itemAt(0).checked = true
+// }
+// }
+// }
+
+ ComboBox {
+ id: camerainfo
+ implicitWidth: part2width
+ font.pixelSize: 25
+ model: cameradevs
+ contentItem: Text {
+ text: camerainfo.displayText
+ font: camerainfo.font
+ color: camerainfo.pressed ? "#17a81a" : "white"
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+ indicator: Canvas {
+ id: canvas
+ x: camerainfo.width - width - camerainfo.rightPadding
+ y: camerainfo.topPadding + (camerainfo.availableHeight - height) / 2
+ width: 20
+ height: 12
+ contextType: "2d"
+
+ Connections {
+ target: camerainfo
+ onPressedChanged: {
+ canvas.requestPaint()
+ properties[0] = camerainfo.displayText
+ console.log("onPressedChanged", properties[0])
+ }
+ }
+
+ onPaint: {
+ context.reset();
+ context.moveTo(0, 0);
+ context.lineTo(width, 0);
+ context.lineTo(width / 2, height);
+ context.closePath();
+ context.fillStyle = camerainfo.pressed ? "#17a81a" : "white";
+ context.fill();
+ }
+ }
+ popup: Popup {
+ id: popup
+ y: camerainfo.height - 1
+ implicitWidth: camerainfo.width
+ implicitHeight: listview.count > 6 ? (listview.contentHeight/3.3) : listview.contentHeight
+ padding: 0
+
+ contentItem: ListView {
+ id: listview
+ clip: true
+ model: camerainfo.visible ? camerainfo.delegateModel : null
+ currentIndex: camerainfo.highlightedIndex
+ ScrollIndicator.vertical: ScrollIndicator { }
+ }
+
+ background: Image { source: "images/camerainfo_bg.svg" }
+ }
+ delegate: ItemDelegate {
+ id: popupdelegate
+ width: camerainfo.width
+ contentItem: Item {
+ implicitHeight: 30
+ Text {
+ text: modelData
+ color: popupdelegate.pressed || highlighted ? "#21be2b" : "white"
+ font: camerainfo.font
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+ highlighted: camerainfo.highlightedIndex == index
+ }
+
+ background: Image { source: "images/camerainfo_bg.svg" }
+
+ onCurrentTextChanged: {
+ properties[0] = camerainfo.currentText
+ console.log("onCurrentTextChanged", properties[0])
+ }
+ }
+
+ Repeater {
+ id: textfields
+ property list<IntValidator> range: [
+ IntValidator{bottom: 0; top: 100;},
+ IntValidator{bottom: 0; top: 2000;},
+ IntValidator{bottom: 0; top: 2000;}
+ ]
+ model: ['(0~100)', '(0~2000)', '(0~2000)']
+ delegate: TextField {
+ implicitWidth: part2width
+ implicitHeight: partheight
+ color: 'white'
+ font.pixelSize: part2fontsize
+ verticalAlignment: TextInput.AlignBottom
+ Label {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ text: model.modelData
+ color: 'gray'
+ font.pixelSize: 20
+ }
+ onTextChanged: {
+ if(text == '0') {
+ text = ''
+ }
+ if(model.index + 1 < properties.length) {
+ properties[model.index + 1] = text
+ }
+ }
+ onFocusChanged: {
+ root.updateKeyBoard()
+ }
+ }
+ Component.onCompleted: {
+ for(var i = 0; i < textfields.count && i < range.length; i++) {
+ textfields.itemAt(i).validator = range[i]
+ root.addTarget(textfields.itemAt(i))
+ }
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ logsaveimpl.enumerateCameras();
+ cameradevs = logsaveimpl.cameradevs();
+ properties[0] = cameradevs[0]
+ console.log(properties[0]);
+ }
+}
diff --git a/app/logfile/images/HMI_Settings_Button_Cancel.svg b/app/logfile/images/HMI_Settings_Button_Cancel.svg
new file mode 100644
index 0000000..4251412
--- /dev/null
+++ b/app/logfile/images/HMI_Settings_Button_Cancel.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;#38;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 151 51"
+ style="enable-background:new 0 0 151 51;"
+ xml:space="preserve"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="HMI_Settings_TimeDate_Button_Cancel.svg"><metadata
+ id="metadata33"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs31" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview29"
+ showgrid="false"
+ inkscape:zoom="4.4503311"
+ inkscape:cx="-69.095982"
+ inkscape:cy="25.5"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><style
+ type="text/css"
+ id="style3">
+ .st0{fill:none;stroke:url(#SVGID_1_);stroke-miterlimit:10;}
+ .st1{opacity:0.3;fill:url(#SVGID_2_);}
+ .st2{fill:#FFFFFF;}
+ .st3{font-family:'Roboto-Regular';}
+ .st4{font-size:20px;}
+ .st5{letter-spacing:3;}
+</style><switch
+ id="switch5"><g
+ i:extraneous="self"
+ id="g7"><g
+ id="g9"><linearGradient
+ id="SVGID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="24.7258"
+ y1="75.9063"
+ x2="126.2742"
+ y2="-24.9063"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop12" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop14" /></linearGradient><rect
+ x="0.5"
+ y="0.5"
+ class="st0"
+ width="150"
+ height="50"
+ id="rect16" /><linearGradient
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="-25.8746"
+ y1="126.14"
+ x2="190.2191"
+ y2="-88.3878"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop19" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop21" /></linearGradient><rect
+ x="0.5"
+ y="0.5"
+ class="st1"
+ width="150"
+ height="50"
+ id="rect23" /></g></g></switch></svg> \ No newline at end of file
diff --git a/app/logfile/images/HMI_Settings_Button_Ok.svg b/app/logfile/images/HMI_Settings_Button_Ok.svg
new file mode 100644
index 0000000..dac68d8
--- /dev/null
+++ b/app/logfile/images/HMI_Settings_Button_Ok.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;#38;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 151 51"
+ style="enable-background:new 0 0 151 51;"
+ xml:space="preserve"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="HMI_Settings_TimeDate_Button_Set.svg"><metadata
+ id="metadata33"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs31" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview29"
+ showgrid="false"
+ inkscape:zoom="4.4503311"
+ inkscape:cx="-106.17187"
+ inkscape:cy="25.5"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><style
+ type="text/css"
+ id="style3">
+ .st0{fill:none;stroke:url(#SVGID_1_);stroke-miterlimit:10;}
+ .st1{opacity:0.84;fill:url(#SVGID_2_);}
+ .st2{fill:#27232B;}
+ .st3{font-family:'Roboto-Regular';}
+ .st4{font-size:20px;}
+ .st5{letter-spacing:3;}
+</style><switch
+ id="switch5"><g
+ i:extraneous="self"
+ id="g7"><g
+ id="g9"><linearGradient
+ id="SVGID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="24.7258"
+ y1="75.9063"
+ x2="126.2742"
+ y2="-24.9063"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop12" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop14" /></linearGradient><rect
+ x="0.5"
+ y="0.5"
+ class="st0"
+ width="150"
+ height="50"
+ id="rect16" /><linearGradient
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="-25.8746"
+ y1="126.14"
+ x2="190.2191"
+ y2="-88.3878"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop19" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop21" /></linearGradient><rect
+ x="0.5"
+ y="0.5"
+ class="st1"
+ width="150"
+ height="50"
+ id="rect23" /></g></g></switch></svg> \ No newline at end of file
diff --git a/app/logfile/images/HMI_Settings_LogFile.svg b/app/logfile/images/HMI_Settings_LogFile.svg
new file mode 100644
index 0000000..5c43c9d
--- /dev/null
+++ b/app/logfile/images/HMI_Settings_LogFile.svg
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;#38;#38;#38;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 100 100"
+ style="enable-background:new 0 0 100 100;"
+ xml:space="preserve"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="LOG2.svg"
+ width="100%"
+ height="100%"><metadata
+ id="metadata20"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs18" /><sodipodi:namedview
+ pagecolor="#54544e"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0.32941176"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1615"
+ inkscape:window-height="917"
+ id="namedview16"
+ showgrid="false"
+ inkscape:zoom="3.337544"
+ inkscape:cx="29.813716"
+ inkscape:cy="60.135712"
+ inkscape:window-x="65"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g7" /><style
+ type="text/css"
+ id="style3">
+ .st0{fill:#FFFFFF;}
+</style><switch
+ id="switch5"><g
+ i:extraneous="self"
+ id="g7"><rect
+ style="fill:#ffffff;fill-opacity:0"
+ id="rect3825"
+ width="39.40678"
+ height="17.79661"
+ x="29.661016"
+ y="23.305084"
+ ry="7.2033896" /><path
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ d="m 25.625,30.9375 c -2.112712,0 -3.84375,1.718453 -3.84375,3.84375 l 0,27.46875 c 0,2.125297 1.731038,3.84375 3.84375,3.84375 l 16.9375,0 c 1.879864,0 3.399091,-1.360464 3.71875,-3.15625 l -16.4375,0 c -2.112712,0 -3.8125,-1.718453 -3.8125,-3.84375 l 0,-27.5 c 0,-0.22376 0.05755,-0.443189 0.09375,-0.65625 l -0.5,0 z"
+ id="rect5439"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ d="m 49.157183,31.15625 c -6.669621,0 -12.094683,7.815468 -12.094683,17.46875 0,9.653282 5.425062,17.46875 12.094683,17.46875 6.66962,0 12.064745,-7.815468 12.064745,-17.46875 0,-9.653282 -5.395125,-17.46875 -12.064745,-17.46875 z m -0.239499,3.28125 c 4.764015,0 8.621952,6.38311 8.621952,14.28125 0,7.89814 -3.857937,14.3125 -8.621952,14.3125 -4.764015,0 -8.621952,-6.41436 -8.621952,-14.3125 0,-7.89814 3.857937,-14.28125 8.621952,-14.28125 z"
+ id="path5496"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ d="M 69.9375 30.84375 C 63.267879 30.84375 57.84375 38.659218 57.84375 48.3125 C 57.84375 57.965782 63.267879 65.78125 69.9375 65.78125 C 76.512166 65.78125 81.849391 58.181821 82 48.71875 L 78.3125 48.71875 C 78.211819 56.472129 74.388192 62.71875 69.6875 62.71875 C 64.923485 62.71875 61.0625 56.30439 61.0625 48.40625 C 61.0625 40.50811 64.923485 34.125 69.6875 34.125 C 73.815246 34.125 77.246493 38.926339 78.09375 45.34375 L 81.8125 45.34375 C 80.839908 37.107133 75.907462 30.84375 69.9375 30.84375 z "
+ id="path5496-9" /><rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ id="rect5539"
+ width="9.7457628"
+ height="4.4491525"
+ x="70.886818"
+ y="48.691174"
+ rx="0.21186557"
+ ry="2.9228771" /><path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ id="path5541"
+ sodipodi:cx="50.186604"
+ sodipodi:cy="65.693336"
+ sodipodi:rx="38.501366"
+ sodipodi:ry="1.9475398"
+ d="m 88.687969,65.693336 a 38.501366,1.9475398 0 1 1 -77.002731,0 38.501366,1.9475398 0 1 1 77.002731,0 z" /></g></switch></svg> \ No newline at end of file
diff --git a/app/logfile/images/Keyboard_Arrow.svg b/app/logfile/images/Keyboard_Arrow.svg
new file mode 100644
index 0000000..0b28afe
--- /dev/null
+++ b/app/logfile/images/Keyboard_Arrow.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 89.6 119.8"
+ style="enable-background:new 0 0 89.6 119.8;"
+ xml:space="preserve"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="Keyboard_Arrow.svg"><metadata
+ id="metadata42"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs40" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview38"
+ showgrid="false"
+ inkscape:zoom="1.9699499"
+ inkscape:cx="-219.92755"
+ inkscape:cy="59.900002"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><style
+ type="text/css"
+ id="style3">
+ .st0{opacity:0.42;fill:#ACACA5;}
+ .st1{opacity:0.42;fill:url(#SVGID_1_);}
+ .st2{fill:none;stroke:#ACACA5;stroke-miterlimit:10;}
+ .st3{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;}
+ .st4{fill:#FFFFFF;}
+</style><switch
+ id="switch5"><g
+ i:extraneous="self"
+ id="g7"><g
+ id="g9"><g
+ id="g28"><g
+ id="g30"><polyline
+ class="st3"
+ points="53.7,44 53.7,71.2 22,71.2 "
+ id="polyline32" /><g
+ id="g34"><path
+ class="st4"
+ d="M35.7,61.6c0.3,0.5,0.2,1.1-0.3,1.4l-12.9,8.2l12.9,8.2c0.5,0.3,0.6,0.9,0.3,1.4 c-0.3,0.5-0.9,0.6-1.4,0.3l-14.2-9.1c-0.3-0.2-0.5-0.5-0.5-0.8c0-0.3,0.2-0.7,0.5-0.8l14.2-9.1c0.2-0.1,0.4-0.2,0.5-0.2 C35.2,61.2,35.5,61.3,35.7,61.6z"
+ id="path36" /></g></g></g></g></g></switch></svg> \ No newline at end of file
diff --git a/app/logfile/images/Keyboard_Back.svg b/app/logfile/images/Keyboard_Back.svg
new file mode 100644
index 0000000..e28b58e
--- /dev/null
+++ b/app/logfile/images/Keyboard_Back.svg
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 78.3 119.8"
+ style="enable-background:new 0 0 78.3 119.8;"
+ xml:space="preserve"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="Keyboard_Inactive_Back.svg"><metadata
+ id="metadata21"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs19" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview17"
+ showgrid="false"
+ inkscape:zoom="1.9699499"
+ inkscape:cx="-229.89238"
+ inkscape:cy="59.900002"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><style
+ type="text/css"
+ id="style3">
+ .st0{opacity:0.42;fill:#ACACA5;}
+ .st1{fill:none;stroke:#ACACA5;stroke-miterlimit:10;}
+ .st2{fill:#FFFFFF;}
+</style><switch
+ id="switch5"><g
+ i:extraneous="self"
+ id="g7"><g
+ id="g9"><path
+ class="st2"
+ d="M61.5,45.1H26.3c-0.5,0-1,0.2-1.3,0.5L10.6,59.9c-0.3,0.3-0.5,0.8-0.5,1.3s0.2,1,0.5,1.3L25,76.9 c0.3,0.3,0.8,0.5,1.3,0.5h35.3c1,0,1.8-0.8,1.8-1.8V46.9C63.4,45.9,62.5,45.1,61.5,45.1z M48.8,65.6c0.9,0.9,0.9,2.2,0,3.1 c-0.8,0.8-2.2,0.8-3.1,0l-4.3-4.3L37,68.7c-0.8,0.9-2.2,0.8-3.1,0c-0.8-0.8-0.9-2.2,0-3.1l4.3-4.3L34,56.9 c-0.8-0.8-0.8-2.2,0-3.1c0.9-0.9,2.2-0.8,3.1,0l4.3,4.3l4.4-4.4c0.8-0.8,2.2-0.8,3.1,0c0.8,0.8,0.8,2.2,0,3.1l-4.4,4.4L48.8,65.6 z"
+ id="path15" /></g></g></switch></svg> \ No newline at end of file
diff --git a/app/logfile/images/camerainfo_bg.svg b/app/logfile/images/camerainfo_bg.svg
new file mode 100644
index 0000000..109ffe7
--- /dev/null
+++ b/app/logfile/images/camerainfo_bg.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;#38;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 600 51"
+ style="enable-background:new 0 0 600 51;"
+ xml:space="preserve"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="HMI_Settings_TimeDate_Button_Cancel.svg"><metadata
+ id="metadata33"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs31" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview29"
+ showgrid="false"
+ inkscape:zoom="4.4503311"
+ inkscape:cx="-69.095982"
+ inkscape:cy="25.5"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><style
+ type="text/css"
+ id="style3">
+ .st0{fill:none;stroke:url(#SVGID_1_);stroke-miterlimit:10;}
+ .st1{opacity:0.3;fill:url(#SVGID_2_);}
+ .st2{fill:#FFFFFF;}
+ .st3{font-family:'Roboto-Regular';}
+ .st4{font-size:20px;}
+ .st5{letter-spacing:3;}
+</style><switch
+ id="switch5"><g
+ i:extraneous="self"
+ id="g7"><g
+ id="g9"><linearGradient
+ id="SVGID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="24.7258"
+ y1="75.9063"
+ x2="126.2742"
+ y2="-24.9063"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop12" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop14" /></linearGradient><rect
+ x="0.5"
+ y="0.5"
+ class="st0"
+ width="599"
+ height="50"
+ id="rect16" /><linearGradient
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="-25.8746"
+ y1="126.14"
+ x2="190.2191"
+ y2="-88.3878"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop19" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop21" /></linearGradient><rect
+ x="0.5"
+ y="0.5"
+ class="st1"
+ width="599"
+ height="50"
+ id="rect23" /></g></g></switch></svg>
diff --git a/app/logfile/keyboard/AbstractKeyboard.qml b/app/logfile/keyboard/AbstractKeyboard.qml
new file mode 100644
index 0000000..fc54aba
--- /dev/null
+++ b/app/logfile/keyboard/AbstractKeyboard.qml
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Window 2.0
+
+Item {
+ id: root
+ implicitWidth: Screen.width
+ implicitHeight: Screen.height / 3
+ signal hide()
+ signal next()
+ property Item target
+}
diff --git a/app/logfile/keyboard/Key.qml b/app/logfile/keyboard/Key.qml
new file mode 100644
index 0000000..390a066
--- /dev/null
+++ b/app/logfile/keyboard/Key.qml
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+
+MouseArea {
+ id: root
+ implicitWidth: 50
+ implicitHeight: 80
+ property string text
+ property alias image: image.source
+ property bool touchable: true
+ property bool checkable: false
+ property bool checked: false
+ property bool capital: false
+
+ onClicked: {
+ if (checkable) {
+ checked = !checked
+ } else {
+ if (label.text.length === 1)
+ insert(label.text)
+ }
+ }
+
+ function clearSelctedText() {
+ if (target && target.selectedText.length > 0) {
+ target.remove(target.selectionStart, target.selectionEnd)
+ return true
+ }
+ return false
+ }
+
+ function insert(text) {
+ clearSelctedText()
+ if(target) {
+ target.insert(target.cursorPosition, text)
+ }
+ }
+
+ Rectangle {
+ id: buttonstyle
+ anchors.fill: parent
+ border.width: 2
+ border.color: root.touchable ? 'white' : 'gray'
+ smooth: true
+ radius: root.height / 10
+ color: 'gray'
+ Rectangle {
+ visible: root.touchable
+ anchors.fill: parent
+ radius: parent.radius
+ opacity: root.pressed || root.checked ? 0 : 0.5
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: 'transparent'
+ }
+ GradientStop {
+ position: 1.0
+ color: '#66FF99'
+ }
+ }
+ }
+ }
+
+ Text {
+ id: label
+ anchors.centerIn: parent
+ color: 'white'
+ font.pixelSize: root.height / 2
+ text: root.text
+ }
+
+ Image {
+ id: image
+ anchors.centerIn: parent
+ scale: 0.8
+ }
+}
diff --git a/app/logfile/keyboard/Keyboard.qml b/app/logfile/keyboard/Keyboard.qml
new file mode 100644
index 0000000..c5734d3
--- /dev/null
+++ b/app/logfile/keyboard/Keyboard.qml
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Window 2.0
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+
+Item {
+ id: root
+ implicitWidth: Screen.width
+ implicitHeight: Screen.height / 3
+ property Item target
+ property AbstractKeyboard active
+
+ visible: false
+
+ Rectangle {
+ anchors.fill: parent
+ color: 'black'
+ opacity: 0.5
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ }
+}
diff --git a/app/logfile/keyboard/Numbers.qml b/app/logfile/keyboard/Numbers.qml
new file mode 100644
index 0000000..664f206
--- /dev/null
+++ b/app/logfile/keyboard/Numbers.qml
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Window 2.0
+import QtQuick.Layouts 1.1
+
+AbstractKeyboard {
+ id: root
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: root.height / 10
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Repeater {
+ model: ['1', '2', '3']
+ delegate: Key {
+ text: model.modelData
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ Key {
+ image: '../images/Keyboard_Back.svg'
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ onClicked: {
+ if (!clearSelctedText()) {
+ if (target && target.cursorPosition > 0)
+ target.remove(target.cursorPosition - 1, target.cursorPosition)
+ }
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Repeater {
+ model: ['4', '5', '6']
+ delegate: Key {
+ text: model.modelData
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ Key {
+ image: '../images/Keyboard_Arrow.svg'
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ onClicked: {
+ root.hide()
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Repeater {
+ model: ['7', '8', '9']
+ delegate: Key {
+ text: model.modelData
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ Key {
+ text: 'Next'
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ onClicked: {
+ root.next()
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Key {
+ text: ' '
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ touchable: false
+ }
+ Key {
+ text: '0'
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ Key {
+ text: ' '
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ touchable: false
+ }
+ Key {
+ text: 'Clear'
+ Layout.preferredWidth: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ onClicked: {
+ target.clear()
+ }
+ }
+ }
+ }
+}
diff --git a/app/logfile/logfile.qrc b/app/logfile/logfile.qrc
new file mode 100644
index 0000000..b93cf0f
--- /dev/null
+++ b/app/logfile/logfile.qrc
@@ -0,0 +1,23 @@
+<RCC>
+ <qresource prefix="/logfile">
+ <file>Bar.qml</file>
+ <file>LogFile.qml</file>
+ <file>LogSave.qml</file>
+ <file>CANLog.qml</file>
+ <file>LogPlay.qml</file>
+ <file>VideoLog.qml</file>
+ <file>AudioLog.qml</file>
+ <file>keyboard/Numbers.qml</file>
+ <file>keyboard/Keyboard.qml</file>
+ <file>keyboard/Key.qml</file>
+ <file>keyboard/AbstractKeyboard.qml</file>
+ <file>images/HMI_Settings_LogFile.svg</file>
+ <file>images/Keyboard_Arrow.svg</file>
+ <file>images/Keyboard_Back.svg</file>
+ <file>images/HMI_Settings_Button_Cancel.svg</file>
+ <file>images/HMI_Settings_Button_Ok.svg</file>
+ <file>TouchLog.qml</file>
+ <file>TouchLogPlay.qml</file>
+ <file>images/camerainfo_bg.svg</file>
+ </qresource>
+</RCC>
diff --git a/app/logfile/logplay.cpp b/app/logfile/logplay.cpp
new file mode 100644
index 0000000..e13c3fc
--- /dev/null
+++ b/app/logfile/logplay.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logplay.h"
+#include <QDir>
+#include <QDirIterator>
+#include <QDateTime>
+#include <QTextStream>
+#include <sys/wait.h>
+#include <unistd.h>
+
+const std::string LogPlayImpl::PlayUtilsExec = "/usr/bin/canplayer";
+
+LogPlayImpl::LogPlayImpl(QObject *parent) : QAbstractItemModel(parent)
+{
+ roles[nameRole] = "name";
+ roles[folderRole] = "folder";
+ roles[childrenRole] = "children";
+ roles[pathRole] = "path";
+
+ playUtils = -1;
+}
+
+LogPlayImpl::~LogPlayImpl()
+{
+ CANPlay(false);
+}
+
+int LogPlayImpl::rowCount(const QModelIndex &parent) const
+{
+ (void)parent;
+ return datas.size();
+}
+
+int LogPlayImpl::columnCount(const QModelIndex &parent) const
+{
+ (void)parent;
+ return 1;
+}
+
+QModelIndex LogPlayImpl::index(int row, int column, const QModelIndex &parent) const
+{
+ (void)parent;
+ if((row >= 0) && (row < datas.size()))
+ {
+ return createIndex(row,column);
+ }
+ else
+ {
+ return QModelIndex();
+ }
+}
+
+QVariant LogPlayImpl::data(const QModelIndex &index, int role) const
+{
+ if(index.isValid() && index.row()<datas.size())
+ {
+ return datas[index.row()][role];
+ }
+ else
+ {
+ QHash<int,QVariant> data;
+ data[nameRole] = "";
+ data[folderRole] = true;
+ data[childrenRole] = 0;
+ return data[role];
+ }
+}
+
+QModelIndex LogPlayImpl::parent(const QModelIndex &child) const
+{
+ (void)child;
+ return QModelIndex();
+}
+
+QHash<int,QByteArray> LogPlayImpl::roleNames() const
+{
+ return roles;
+}
+
+void LogPlayImpl::refresh()
+{
+ QDir dir(QDir::homePath() + "/" + "LogDataFile");
+
+ if(dir.exists())
+ {
+ beginResetModel();
+ dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
+ dir.setSorting(QDir::Name);
+
+ QFileInfoList list = dir.entryInfoList();
+ datas.clear();
+
+ for(int i = 0; i < list.size(); i++)
+ {
+ QHash<int,QVariant> parent;
+ QStringList children;
+
+ parent[nameRole] = list.at(i).fileName();
+ parent[folderRole] = true;
+ parent[pathRole] = list.at(i).absoluteFilePath();
+
+ children.clear();
+
+ QDirIterator it(list.at(i).filePath(), QStringList() << "*.log", QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
+ while(it.hasNext())
+ {
+ children << it.next();
+ }
+
+ parent[childrenRole] = children.count();
+ datas.append(parent);
+
+ if(children.count() > 0)
+ {
+ children.sort();
+
+ for(int j = 0; j < children.count(); j++)
+ {
+ QHash<int,QVariant> child;
+
+ child[nameRole] = children.at(j).section("/", -1);
+ child[folderRole] = false;
+ child[childrenRole] = 0;
+ child[pathRole] = children.at(j);
+
+ datas.append(child);
+ }
+ }
+ }
+ endResetModel();
+ }
+}
+
+void LogPlayImpl::setLogFileIndex(int index)
+{
+ startTime.clear();
+ endTime.clear();
+ portsList.clear();
+
+ if(index > 0 && index < datas.size())
+ {
+ QFile file(datas[index][pathRole].toString());
+
+ if(file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ QTextStream filetxt(&file);
+ QString line = "";
+ QString port = "";
+ QDateTime datetime;
+
+ while(!filetxt.atEnd())
+ {
+ line = filetxt.readLine();
+ if(!line.isEmpty())
+ {
+ port = line.section(" ", 1, 1, QString::SectionSkipEmpty);
+ if(port.isEmpty())
+ {
+ port = line.section("\t", 1, 1, QString::SectionSkipEmpty);
+ }
+ datetime = QDateTime::fromTime_t(line.mid(line.indexOf("(")+1, line.indexOf(".")-line.indexOf("(")-1).toUInt());
+ if(!portsList.contains(port))
+ {
+ portsList << port;
+ startTime[port] << datetime.toString("yyyy") << datetime.toString("MM") << datetime.toString("dd")
+ << datetime.toString("hh") << datetime.toString("mm") << datetime.toString("ss");
+ }
+ if(endTime.find(port) != endTime.end())
+ {
+ endTime[port].clear();
+ }
+ endTime[port] << datetime.toString("yyyy") << datetime.toString("MM") << datetime.toString("dd")
+ << datetime.toString("hh") << datetime.toString("mm") << datetime.toString("ss");
+ }
+ }
+ file.close();
+ }
+ }
+}
+
+QStringList LogPlayImpl::getCanLogTime(QString port)
+{
+ if(startTime.find(port) != startTime.end() && endTime.find(port) != endTime.end())
+ {
+ return startTime[port] + endTime[port];
+ }
+ else
+ {
+ return QStringList();
+ }
+}
+
+QStringList LogPlayImpl::getPortsList()
+{
+ return portsList;
+}
+
+bool LogPlayImpl::CANPlay(bool play)
+{
+ if(play)
+ {
+ playUtils = fork();
+ switch(playUtils)
+ {
+ case 0:
+ execl(PlayUtilsExec.c_str(), basename(PlayUtilsExec.c_str()),
+ playUtilsArg["-p"].toStdString().c_str(),
+ "-I", playUtilsArg["-I"].toStdString().c_str(),
+ "-l", "i",
+ (char*)NULL);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if((playUtils > 0) && (kill(playUtils, SIGUSR1) == 0))
+ {
+ waitpid(playUtils, NULL, 0);
+ }
+ }
+
+ return true;
+}
+
+void LogPlayImpl::setCANProperty(int index, QString port, QString stime, QString etime)
+{
+ if(index > 0 && index < datas.size())
+ {
+ playUtilsArg["-p"] = "vcan0="+port;
+ playUtilsArg["-I"] = datas[index][pathRole].toString();
+ }
+}
+
+bool LogPlayImpl::checkTime(QString port, QString stime, QString etime)
+{
+ QString maxtime = "";
+ QString mintime = "";
+
+ if(startTime.find(port) == startTime.end() || endTime.find(port) == endTime.end())
+ {
+ return false;
+ }
+
+ for(int idx = 0; idx < startTime[port].length() && idx < endTime[port].length(); idx++)
+ {
+ mintime += startTime[port].at(idx) + (idx == startTime[port].length() - 1 ? "" : ",");
+ maxtime += endTime[port].at(idx) + (idx == endTime[port].length() - 1 ? "" : ",");
+ }
+
+ if((stime < mintime) || (stime > maxtime) || (etime < mintime) || (etime > maxtime) || (stime > etime))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+QString LogPlayImpl::convertTimeFormat(QString time)
+{
+ QStringList sep;
+ QString convertstr = "";
+
+ sep << "-" << "-" << "." << ":" << ":";
+
+ //2016-02-08.09:41:59
+ for(int idx = 0; idx <= time.count(","); idx++)
+ {
+ convertstr += time.section(',' , idx, idx) + (idx < sep.length() ? sep.at(idx) : "");
+ }
+
+ return convertstr;
+}
diff --git a/app/logfile/logplay.h b/app/logfile/logplay.h
new file mode 100644
index 0000000..90a35c1
--- /dev/null
+++ b/app/logfile/logplay.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOGPLAYIMPPL_H
+#define LOGPLAYIMPPL_H
+
+#include <QAbstractItemModel>
+
+class LogPlayImpl: public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ explicit LogPlayImpl(QObject *parent = 0);
+ ~LogPlayImpl();
+
+ enum LogPlayImplRoles{
+ nameRole=Qt::UserRole+1,
+ folderRole,
+ childrenRole,
+ pathRole
+ };
+
+ int rowCount(const QModelIndex &parent=QModelIndex()) const Q_DECL_OVERRIDE;
+ int columnCount(const QModelIndex &parent=QModelIndex()) const Q_DECL_OVERRIDE;
+ QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const Q_DECL_OVERRIDE;
+ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const Q_DECL_OVERRIDE;
+ QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE;
+ QHash<int,QByteArray> roleNames() const Q_DECL_OVERRIDE;
+
+ Q_INVOKABLE void refresh();
+ Q_INVOKABLE bool CANPlay(bool play);
+ Q_INVOKABLE void setCANProperty(int index, QString port, QString stime, QString etime);
+ Q_INVOKABLE void setLogFileIndex(int index);
+ Q_INVOKABLE QStringList getPortsList();
+ Q_INVOKABLE QStringList getCanLogTime(QString port);
+ Q_INVOKABLE bool checkTime(QString port, QString stime, QString etime);
+
+private:
+ QString convertTimeFormat(QString time);
+
+private:
+ QHash<int,QByteArray> roles;
+ QList<QHash<int,QVariant>> datas;
+ std::map<QString, QStringList> startTime;
+ std::map<QString, QStringList> endTime;
+ QStringList portsList;
+
+ static const std::string PlayUtilsExec;
+
+ pid_t playUtils;
+ std::map<QString, QString> playUtilsArg;
+};
+
+#endif // LOGPLAYIMPPL_H
diff --git a/app/logfile/logsave.cpp b/app/logfile/logsave.cpp
new file mode 100644
index 0000000..0c21afe
--- /dev/null
+++ b/app/logfile/logsave.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logsave.h"
+
+#include <QDateTime>
+#include <QDir>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <opencv2/imgproc.hpp>
+#include <linux/videodev2.h>
+
+#include <QDebug>
+#include <QCoreApplication>
+
+const QString LogSaveImpl::TripPre = "Trip_";
+const QString LogSaveImpl::SegPre = "Seg_";
+
+const QString LogSaveImpl::FolderAudio = "AUDIO";
+const QString LogSaveImpl::FolderVideo = "VIDEO";
+const QString LogSaveImpl::FolderCAN = "CAN";
+const QString LogSaveImpl::FolderTouch = "TOUCH";
+const QString LogSaveImpl::FolderOther = "OTHER";
+
+const QString LogSaveImpl::RootFolder = "LogDataFile";
+
+const std::string LogSaveImpl::DumpUtilsExec = "/usr/bin/candump";
+const std::string LogSaveImpl::AudioMixerExec = "/usr/bin/amixer";
+const std::string LogSaveImpl::AudioUtilsExec = "/usr/bin/arecord";
+const std::string LogSaveImpl::VideoUtilsExec = std::string(getenv("AFM_APP_INSTALL_DIR")) + "/bin/videoutils";
+const std::string LogSaveImpl::TouchRecUtilsExec = std::string(getenv("AFM_APP_INSTALL_DIR")) + "/bin/tsrecorder";
+
+LogSaveImpl::LogSaveImpl(QObject *parent):QObject(parent)
+{
+// videoDevice["None"] = "10";
+// videoDevice["Front"] = "1";
+// videoDevice["Right"] = "2";
+// videoDevice["Left"] = "3";
+// videoDevice["Rear"] = "4";
+
+ logFolders[FolderAudio] = "";
+ logFolders[FolderVideo] = "";
+ logFolders[FolderCAN] = "";
+ logFolders[FolderOther] = "";
+ logFolders[FolderTouch] = "";
+
+ rootPath = QDir::homePath() + "/" + RootFolder;
+ QDir rootdir(rootPath);
+ if(!rootdir.exists())
+ {
+ if(!rootdir.mkdir(rootPath))
+ {
+ rootPath = "/tmp";
+ }
+ }
+
+ dumpUtils = -1;
+ audioMixer = -1;
+ audioUtils = -1;
+ videoUtils = -1;
+ touchRecUtils = -1;
+}
+
+LogSaveImpl::~LogSaveImpl()
+{
+ saveStop();
+}
+
+void LogSaveImpl::setVideoProperty(QStringList properties)
+{
+ if(properties.length() < 4)
+ {
+ return;
+ }
+
+// videoUtilsArg["-d"] = videoDevice[properties[0]];
+ videoUtilsArg["-d"] = properties[0].remove("/dev/video");
+ videoUtilsArg["-r"] = (properties[1] != "0" ? properties[1] : "30");
+ videoUtilsArg["-w"] = (properties[2] != "0" ? properties[2] : "640");
+ videoUtilsArg["-h"] = (properties[3] != "0" ? properties[3] : "480");
+ videoUtilsArg["-f"] = logFolders[FolderVideo] + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss") + ".avi";
+}
+
+void LogSaveImpl::setCANProperty()
+{
+ dumpUtilsArg["-L"] = logFolders[FolderCAN] + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss") + ".log";
+}
+
+void LogSaveImpl::setTouchProperty()
+{
+ touchRecUtilsArg["-f"] = logFolders[FolderTouch] + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss") + ".touch";
+}
+
+void LogSaveImpl::setAudioProperty(QString sps)
+{
+ audioUtilsArg["-r"] = (sps != "0" ? sps : "44100");
+ audioUtilsArg["-f"] = logFolders[FolderAudio] + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss") + ".wav";
+}
+
+bool LogSaveImpl::saveStart(bool can, bool touch, bool video, bool audio)
+{
+ qDebug("touch:%d", touch);
+ if(video)
+ {
+ qDebug("-d:%s", videoUtilsArg["-d"].toStdString().c_str());
+ videoUtils = fork();
+ switch(videoUtils)
+ {
+ case 0:
+ execl(VideoUtilsExec.c_str(), basename(VideoUtilsExec.c_str()),
+ "-d", videoUtilsArg["-d"].toStdString().c_str(),
+ "-w", videoUtilsArg["-w"].toStdString().c_str(),
+ "-h", videoUtilsArg["-h"].toStdString().c_str(),
+ "-r", videoUtilsArg["-r"].toStdString().c_str(),
+ "-f", videoUtilsArg["-f"].toStdString().c_str(),
+ "-c", "MP42",
+ (char *)NULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(can)
+ {
+ int fd;
+ dumpUtils = fork();
+ switch(dumpUtils)
+ {
+ case 0:
+ fd = open(dumpUtilsArg["-L"].toStdString().c_str(), O_CREAT | O_WRONLY, 0666);
+ dup2(fd, 1);
+ close(fd); /* fd は以後不要なので close() */
+ execl(DumpUtilsExec.c_str(), basename(DumpUtilsExec.c_str()), "vcan0", "-L", (char *)NULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(touch)
+ {
+ qDebug() << "current file" << TouchRecUtilsExec.c_str();
+
+ touchRecUtils = fork();
+ qDebug("touchRecUtils:%d", touchRecUtils);
+ switch(touchRecUtils)
+ {
+ case 0:
+ qDebug("TouchRecUtilsExec:%s", touchRecUtilsArg["-f"].toStdString().c_str());
+ execl(TouchRecUtilsExec.c_str(), basename(TouchRecUtilsExec.c_str()), "-f", touchRecUtilsArg["-f"].toStdString().c_str(), (char *)NULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(audio)
+ {
+ // Set volume
+ audioMixer = fork();
+ switch(audioMixer)
+ {
+ case 0:
+ execl(AudioMixerExec.c_str(), basename(AudioMixerExec.c_str()), "-D", "hw:ak4613", "cset", "name='DVC In Capture Volume'", "100%", (char*)NULL);
+ break;
+ default:
+ break;
+ }
+ if(audioMixer > 0)
+ {
+ waitpid(audioMixer, NULL, 0);
+ }
+
+ // Set mute off
+ audioMixer = fork();
+ switch(audioMixer)
+ {
+ case 0:
+ execl(AudioMixerExec.c_str(), basename(AudioMixerExec.c_str()), "-D", "hw:ak4613", "cset", "name='DVC In Mute Switch'", "off", (char*)NULL);
+ break;
+ default:
+ break;
+ }
+ if(audioMixer > 0)
+ {
+ waitpid(audioMixer, NULL, 0);
+ }
+
+ // Start record
+ audioUtils = fork();
+ switch(audioUtils)
+ {
+ case 0:
+ execl(AudioUtilsExec.c_str(), basename(AudioUtilsExec.c_str()), "-c", "2", "-f", "S24_LE", "-r", audioUtilsArg["-r"].toStdString().c_str(), "-t", "wav", "-D", "hw:ak4613", audioUtilsArg["-f"].toStdString().c_str(), (char*)NULL);
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+void LogSaveImpl::saveStop()
+{
+ if((videoUtils > 0) && (kill(videoUtils, SIGUSR1) == 0))
+ {
+ waitpid(videoUtils, NULL, 0);
+ }
+
+ if((dumpUtils > 0) && (kill(dumpUtils, SIGUSR1) == 0))
+ {
+ waitpid(dumpUtils, NULL, 0);
+ }
+
+ if((audioUtils > 0) && (kill(audioUtils, SIGKILL) == 0))
+ {
+ waitpid(audioUtils, NULL, 0);
+ }
+
+ if((touchRecUtils > 0) && (kill(touchRecUtils, SIGKILL) == 0))
+ {
+ waitpid(touchRecUtils, NULL, 0);
+ }
+}
+
+bool LogSaveImpl::createLogFolders()
+{
+ bool result = false;
+
+ if(createSegFolder())
+ {
+ QString path;
+ QDir dir;
+
+ result = true;
+
+ for(std::map<QString,QString>::iterator it = logFolders.begin(); it != logFolders.end(); it++)
+ {
+ path = currentSeg + "/" + it->first;
+ if(!dir.exists(path) && !dir.mkdir(path))
+ {
+ it->second = "";
+ }
+ else
+ {
+ it->second = path + "/";
+ }
+ }
+ }
+ return result;
+}
+
+bool LogSaveImpl::createTripFolder()
+{
+ currentTrip = "";
+ if(!rootPath.isEmpty())
+ {
+ QDir tripdir;
+
+ currentTrip = rootPath + "/" + TripPre + QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
+
+ if(!tripdir.exists(currentTrip) && !tripdir.mkdir(currentTrip))
+ {
+ currentTrip = "";
+ }
+ }
+
+ if(currentTrip.isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+bool LogSaveImpl::createSegFolder()
+{
+ currentSeg = "";
+
+ if(!currentTrip.isEmpty())
+ {
+ QDir segdir;
+
+ currentSeg = currentTrip + "/" + SegPre + QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
+
+ if(!segdir.exists(currentSeg) && !segdir.mkdir(currentSeg))
+ {
+ currentSeg = "";
+ }
+ }
+
+ if(currentSeg.isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void LogSaveImpl::enumerateCameras() {
+ int maxID = 11;
+ for (int idx = 0; idx <maxID; idx++){
+ std::stringstream no;
+ no << "/dev/video" << idx;
+ int fd = open(no.str().c_str(), O_RDWR);
+ if (fd != -1){
+ struct v4l2_capability cap;
+
+ if (ioctl(fd,VIDIOC_QUERYCAP,&cap) != -1){
+ if ((cap.capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING)) == (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING)){
+ qDebug("cap.driver %s, cap.card %s, cap.bus_info %s", (char*)(cap.driver), (char*)(cap.card), (char*)(cap.bus_info));
+ if(strcmp((char*)cap.driver, "uvcvideo") == 0)
+ {
+ cameras.push_back(no.str().c_str()); // vector of all available cameras
+ }
+ }
+ }
+ close(fd);
+ }
+ }
+}
+
+QVariantList LogSaveImpl::cameradevs() const{
+ return cameras;
+}
+
+int LogSaveImpl::cameraCnt() {
+ return cameras.length();
+}
+
+QString LogSaveImpl::getCurrentTrip()
+{
+ return currentTrip;
+}
+
+QString LogSaveImpl::getCurrentSeg()
+{
+ return currentSeg;
+}
diff --git a/app/logfile/logsave.h b/app/logfile/logsave.h
new file mode 100644
index 0000000..45f5829
--- /dev/null
+++ b/app/logfile/logsave.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOGSAVE_H
+#define LOGSAVE_H
+
+#include <QObject>
+#include <QVariantList>
+
+class LogSaveImpl : public QObject
+{
+ Q_OBJECT
+public:
+ explicit LogSaveImpl(QObject *parent = 0);
+ ~LogSaveImpl();
+
+public:
+ Q_INVOKABLE bool saveStart(bool can, bool touch, bool video, bool audio);
+ Q_INVOKABLE void saveStop();
+ Q_INVOKABLE void setCANProperty();
+ Q_INVOKABLE void setTouchProperty();
+ Q_INVOKABLE void setVideoProperty(QStringList properties);
+ Q_INVOKABLE void setAudioProperty(QString sps);
+ Q_INVOKABLE QString getCurrentTrip();
+ Q_INVOKABLE QString getCurrentSeg();
+ Q_INVOKABLE bool createTripFolder();
+ Q_INVOKABLE bool createLogFolders();
+ Q_INVOKABLE QVariantList cameradevs() const;
+ Q_INVOKABLE void enumerateCameras();
+ Q_INVOKABLE int cameraCnt();
+
+private:
+ bool createSegFolder();
+
+signals:
+ void camraCntChanged(const QVariantList& camcnt);
+
+private:
+ static const QString TripPre;
+ static const QString SegPre;
+
+ static const QString FolderAudio;
+ static const QString FolderVideo;
+ static const QString FolderCAN;
+ static const QString FolderTouch;
+ static const QString FolderOther;
+
+ static const QString RootFolder;
+
+ static const std::string DumpUtilsExec;
+ static const std::string AudioMixerExec;
+ static const std::string AudioUtilsExec;
+ static const std::string VideoUtilsExec;
+ static const std::string TouchRecUtilsExec;
+
+ QString currentSeg;
+ QString currentTrip;
+ QString rootPath;
+// std::map<QString, QString> videoDevice;
+ std::map<QString, QString> logFolders;
+
+ pid_t dumpUtils;
+ pid_t audioMixer;
+ pid_t audioUtils;
+ pid_t videoUtils;
+ pid_t touchRecUtils;
+
+ QVariantList cameras;
+
+ std::map<QString, QString> dumpUtilsArg;
+ std::map<QString, QString> audioUtilsArg;
+ std::map<QString, QString> videoUtilsArg;
+ std::map<QString, QString> touchRecUtilsArg;
+};
+
+#endif // LOGSAVE_H
diff --git a/app/logfile/touchlogplay.cpp b/app/logfile/touchlogplay.cpp
new file mode 100644
index 0000000..02a53b4
--- /dev/null
+++ b/app/logfile/touchlogplay.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "touchlogplay.h"
+#include <QDir>
+#include <QDirIterator>
+#include <QDateTime>
+#include <QTextStream>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <QDebug>
+#include <QCoreApplication>
+
+const std::string TouchLogPlayImpl::touchPlayUtilsExec = std::string(getenv("AFM_APP_INSTALL_DIR")) + "/bin/tsplayer";
+TouchLogPlayImpl *TouchLogPlayImpl::pointer = NULL;
+bool TouchLogPlayImpl::touchPlayFlag = false;
+void (*TouchLogPlayImpl::oldHandler)(int) = NULL;
+
+TouchLogPlayImpl::TouchLogPlayImpl(QObject *parent) : QAbstractItemModel(parent)
+{
+ roles[nameRole] = "name";
+ roles[folderRole] = "folder";
+ roles[childrenRole] = "children";
+ roles[pathRole] = "path";
+
+ touchPlayUtils = -1;
+ pointer = this;
+}
+
+TouchLogPlayImpl::~TouchLogPlayImpl()
+{
+ touchPlay(false);
+}
+
+int TouchLogPlayImpl::rowCount(const QModelIndex &parent) const
+{
+ (void)parent;
+ return datas.size();
+}
+
+int TouchLogPlayImpl::columnCount(const QModelIndex &parent) const
+{
+ (void)parent;
+ return 1;
+}
+
+QModelIndex TouchLogPlayImpl::index(int row, int column, const QModelIndex &parent) const
+{
+ (void)parent;
+ if((row >= 0) && (row < datas.size()))
+ {
+ return createIndex(row,column);
+ }
+ else
+ {
+ return QModelIndex();
+ }
+}
+
+QVariant TouchLogPlayImpl::data(const QModelIndex &index, int role) const
+{
+ if(index.isValid() && index.row()<datas.size())
+ {
+ return datas[index.row()][role];
+ }
+ else
+ {
+ QHash<int,QVariant> data;
+ data[nameRole] = "";
+ data[folderRole] = true;
+ data[childrenRole] = 0;
+ return data[role];
+ }
+}
+
+QModelIndex TouchLogPlayImpl::parent(const QModelIndex &child) const
+{
+ (void)child;
+ return QModelIndex();
+}
+
+QHash<int,QByteArray> TouchLogPlayImpl::roleNames() const
+{
+ return roles;
+}
+
+void TouchLogPlayImpl::refresh()
+{
+ QDir dir(QDir::homePath() + "/" + "LogDataFile");
+
+ if(dir.exists())
+ {
+ beginResetModel();
+ dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
+ dir.setSorting(QDir::Name);
+
+ QFileInfoList list = dir.entryInfoList();
+ datas.clear();
+
+ for(int i = 0; i < list.size(); i++)
+ {
+ QHash<int,QVariant> parent;
+ QStringList children;
+
+ parent[nameRole] = list.at(i).fileName();
+ parent[folderRole] = true;
+ parent[pathRole] = list.at(i).absoluteFilePath();
+
+ children.clear();
+
+ QDirIterator it(list.at(i).filePath(), QStringList() << "*.touch", QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
+ while(it.hasNext())
+ {
+ children << it.next();
+ }
+
+ parent[childrenRole] = children.count();
+ datas.append(parent);
+
+ if(children.count() > 0)
+ {
+ children.sort();
+
+ for(int j = 0; j < children.count(); j++)
+ {
+ QHash<int,QVariant> child;
+
+ child[nameRole] = children.at(j).section("/", -1);
+ child[folderRole] = false;
+ child[childrenRole] = 0;
+ child[pathRole] = children.at(j);
+
+ datas.append(child);
+ }
+ }
+ }
+ endResetModel();
+ }
+}
+
+void TouchLogPlayImpl::setLogFileIndex(int index)
+{
+ if(index > 0 && index < datas.size())
+ {
+ QFile file(datas[index][pathRole].toString());
+
+ if(file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ QTextStream filetxt(&file);
+ QString line = "";
+ QString port = "";
+ QDateTime datetime;
+
+ while(!filetxt.atEnd())
+ {
+ line = filetxt.readLine();
+ if(!line.isEmpty())
+ {
+ port = line.section(" ", 1, 1, QString::SectionSkipEmpty);
+ if(port.isEmpty())
+ {
+ port = line.section("\t", 1, 1, QString::SectionSkipEmpty);
+ }
+ datetime = QDateTime::fromTime_t(line.mid(line.indexOf("(")+1, line.indexOf(".")-line.indexOf("(")-1).toUInt());
+ }
+ }
+ file.close();
+ }
+ }
+}
+
+bool TouchLogPlayImpl::touchPlay(bool play)
+{
+ qDebug() << "touchPlayFlag:" << touchPlayFlag;
+ if(play)
+ {
+ oldHandler = signal(SIGCHLD, finalizeChild);
+ if(oldHandler == SIG_ERR)
+ {
+ qDebug("signal error.");
+ return false;
+ }
+
+ qDebug() << "current file" << touchPlayUtilsExec.c_str();
+
+ touchPlayUtils = fork();
+ switch(touchPlayUtils)
+ {
+ case 0:
+ execl(touchPlayUtilsExec.c_str(), basename(touchPlayUtilsExec.c_str()),
+ "-d", "1",
+ "-f", touchplayUtilsArg["-f"].toStdString().c_str(),
+ "-e", "1",
+ (char*)NULL);
+ break;
+ default:
+ touchPlayFlag = true;
+ emit propTouchPlayFlagChanged();
+ qDebug() << "default:";
+ break;
+ }
+ }
+ else
+ {
+ if(touchPlayFlag)
+ {
+ if((touchPlayUtils > 0) && (kill(touchPlayUtils, SIGUSR1) == 0))
+ {
+ waitpid(touchPlayUtils, NULL, 0);
+ }
+ }
+ }
+
+ return true;
+}
+
+void TouchLogPlayImpl::setTouchProperty(int index)
+{
+ if(index > 0 && index < datas.size())
+ {
+ touchplayUtilsArg["-f"] = datas[index][pathRole].toString();
+ }
+}
+
+bool TouchLogPlayImpl::propTouchPlayFlag() const
+{
+ return touchPlayFlag;
+}
+
+void TouchLogPlayImpl::propTouchPlayFlagChangedDelegate()
+{
+ emit propTouchPlayFlagChanged();
+}
+
+void TouchLogPlayImpl::finalizeChild(int sig)
+{
+ if(sig == SIGCHLD)
+ {
+ qDebug("finalizeChild start.");
+ waitpid(-1, NULL, 0);
+ touchPlayFlag = false;
+ pointer->propTouchPlayFlagChangedDelegate();
+ qDebug("finalizeChild end.");
+ }
+
+ if(signal(SIGCHLD, oldHandler) == SIG_ERR)
+ {
+ qDebug("signal error.");
+ }
+}
+
+void TouchLogPlayImpl::emitSignalAgent()
+{
+ qDebug("emitSignalAgent");
+ pointer->propTouchPlayFlagChangedDelegate();
+}
diff --git a/app/logfile/touchlogplay.h b/app/logfile/touchlogplay.h
new file mode 100644
index 0000000..9114de5
--- /dev/null
+++ b/app/logfile/touchlogplay.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TOUCHLOGPLAY_H
+#define TOUCHLOGPLAY_H
+
+#include <QAbstractItemModel>
+
+class TouchLogPlayImpl: public QAbstractItemModel
+{
+ Q_OBJECT
+ Q_PROPERTY(bool propTouchPlayFlag READ propTouchPlayFlag NOTIFY propTouchPlayFlagChanged)
+public:
+ explicit TouchLogPlayImpl(QObject *parent = 0);
+ ~TouchLogPlayImpl();
+
+ enum TouchLogPlayImplRoles {
+ nameRole=Qt::UserRole+1,
+ folderRole,
+ childrenRole,
+ pathRole
+ };
+
+ int rowCount(const QModelIndex &parent=QModelIndex()) const Q_DECL_OVERRIDE;
+ int columnCount(const QModelIndex &parent=QModelIndex()) const Q_DECL_OVERRIDE;
+ QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const Q_DECL_OVERRIDE;
+ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const Q_DECL_OVERRIDE;
+ QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE;
+ QHash<int,QByteArray> roleNames() const Q_DECL_OVERRIDE;
+
+ bool propTouchPlayFlag() const;
+
+ Q_INVOKABLE void refresh();
+ Q_INVOKABLE bool touchPlay(bool play);
+ Q_INVOKABLE void setTouchProperty(int index);
+ Q_INVOKABLE void setLogFileIndex(int index);
+
+ void propTouchPlayFlagChangedDelegate();
+ static void finalizeChild(int sig);
+ static void emitSignalAgent();
+
+signals:
+ void propTouchPlayFlagChanged();
+
+private:
+ QHash<int,QByteArray> roles;
+ QList<QHash<int,QVariant>> datas;
+
+ static const std::string touchPlayUtilsExec;
+ static void (*oldHandler)(int);
+
+ pid_t touchPlayUtils;
+ std::map<QString, QString> touchplayUtilsArg;
+
+ static TouchLogPlayImpl *pointer;
+ static bool touchPlayFlag;
+};
+
+#endif // TOUCHLOGPLAY_H
diff --git a/app/main.cpp b/app/main.cpp
new file mode 100644
index 0000000..7473847
--- /dev/null
+++ b/app/main.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <QtCore/QDebug>
+#include <QtCore/QCommandLineParser>
+#include <QtCore/QUrlQuery>
+#include <QtCore/QFile>
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlApplicationEngine>
+#include <QtQml/QQmlContext>
+#include <QtQuickControls2/QQuickStyle>
+#include <QQuickWindow>
+#include <libhomescreen.hpp>
+#include <network.h>
+#include <qlibwindowmanager.h>
+
+#include "logfile/logsave.h"
+#include "logfile/logplay.h"
+#include "logfile/touchlogplay.h"
+
+int main(int argc, char *argv[])
+{
+ QString graphic_role = QString("log-utils"); // defined in layers.json in window manager
+
+ QGuiApplication app(argc, argv);
+ app.setApplicationName(graphic_role);
+ app.setApplicationVersion(QStringLiteral("0.1.0"));
+ app.setOrganizationDomain(QStringLiteral("automotivelinux.org"));
+ app.setOrganizationName(QStringLiteral("AutomotiveGradeLinux"));
+
+ QQuickStyle::setStyle("AGL");
+
+ QCommandLineParser parser;
+ parser.addPositionalArgument("port", app.translate("main", "port for binding"));
+ parser.addPositionalArgument("secret", app.translate("main", "secret for binding"));
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.process(app);
+ QStringList positionalArguments = parser.positionalArguments();
+
+ qmlRegisterType<LogSaveImpl>("LogSaveImpl", 1, 0, "LogSaveImpl");
+ qmlRegisterType<LogPlayImpl>("LogPlayImpl", 1, 0, "LogPlayImpl");
+ qmlRegisterType<TouchLogPlayImpl>("TouchLogPlayImpl", 1, 0, "TouchLogPlayImpl");
+
+ QQmlApplicationEngine engine;
+ if (positionalArguments.length() != 2) {
+ exit(EXIT_FAILURE);
+ }
+ int port = positionalArguments.takeFirst().toInt();
+ QString secret = positionalArguments.takeFirst();
+ QUrlQuery query;
+ query.addQueryItem(QStringLiteral("token"), secret);
+
+ QUrl bindingAddressWS;
+ bindingAddressWS.setScheme(QStringLiteral("ws"));
+ bindingAddressWS.setHost(QStringLiteral("localhost"));
+ bindingAddressWS.setPort(port);
+ bindingAddressWS.setPath(QStringLiteral("/api"));
+ bindingAddressWS.setQuery(query);
+ QQmlContext *context = engine.rootContext();
+ context->setContextProperty(QStringLiteral("bindingAddressWS"), bindingAddressWS);
+ context->setContextProperty("network", new Network(bindingAddressWS, context));
+
+ std::string token = secret.toStdString();
+ LibHomeScreen* hs = new LibHomeScreen();
+ QLibWindowmanager* qwm = new QLibWindowmanager();
+
+ // WindowManager
+ if(qwm->init(port,secret) != 0) {
+ exit(EXIT_FAILURE);
+ }
+ AGLScreenInfo screenInfo(qwm->get_scale_factor());
+ // Request a surface as described in layers.json windowmanager’s file
+ if(qwm->requestSurface(graphic_role) != 0) {
+ exit(EXIT_FAILURE);
+ }
+ // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs
+ qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, &graphic_role](json_object *object) {
+ fprintf(stderr, "Surface got syncDraw!\n");
+ qwm->endDraw(graphic_role);
+ });
+
+ // HomeScreen
+ hs->init(port, token.c_str());
+ // Set the event handler for Event_ShowWindow which will activate the surface for windowmanager
+ hs->set_event_handler(LibHomeScreen::Event_ShowWindow, [qwm, &graphic_role](json_object *object){
+ qDebug("Surface %s got showWindow\n", graphic_role.toStdString().c_str());
+ qwm->activateWindow(graphic_role);
+ TouchLogPlayImpl::emitSignalAgent();
+ });
+
+ QFile version("/proc/version");
+ if (version.open(QFile::ReadOnly)) {
+ QStringList data = QString::fromLocal8Bit(version.readAll()).split(QLatin1Char(' '));
+ engine.rootContext()->setContextProperty("kernel", data.at(2));
+ version.close();
+ } else {
+ qWarning() << version.errorString();
+ }
+
+ QFile aglversion("/etc/os-release");
+ if (aglversion.open(QFile::ReadOnly)) {
+ QStringList data = QString::fromLocal8Bit(aglversion.readAll()).split(QLatin1Char('\n'));
+ QStringList data2 = data.at(2).split(QLatin1Char('"'));
+ engine.rootContext()->setContextProperty("ucb", data2.at(1));
+ aglversion.close();
+ } else {
+ qWarning() << aglversion.errorString();
+ }
+
+ engine.rootContext()->setContextProperty(QStringLiteral("screenInfo"), &screenInfo);
+ engine.load(QUrl(QStringLiteral("qrc:/logfile/LogFile.qml")));
+ QObject *root = engine.rootObjects().first();
+ QQuickWindow *window = qobject_cast<QQuickWindow *>(root);
+ QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateWindow()));
+
+ return app.exec();
+}
diff --git a/autobuild/agl/autobuild b/autobuild/agl/autobuild
new file mode 100755
index 0000000..e87a1c3
--- /dev/null
+++ b/autobuild/agl/autobuild
@@ -0,0 +1,58 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015 - 2018 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+THISFILE := $(lastword $(MAKEFILE_LIST))
+BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build)
+DEST := ${BUILD_DIR}
+
+.PHONY: all clean distclean configure build package help
+
+all: help
+
+help:
+ @echo "List of targets available:"
+ @echo ""
+ @echo "- all"
+ @echo "- clean"
+ @echo "- distclean"
+ @echo "- configure"
+ @echo "- build: compilation, link and prepare files for package into a widget"
+ @echo "- package: output a widget file '*.wgt'"
+ @echo "- install: install in your INSTALL_ROOT directory"
+ @echo ""
+ @echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt"
+ @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+clean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean
+
+distclean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} distclean) || echo Nothing to distclean
+
+configure:
+ @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+ @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && qmake ${CONFIGURE_ARGS} ..)
+
+build: configure
+ @make -C ${BUILD_DIR} ${BUILD_ARGS} all
+
+package: build
+ @if [ "${DEST}" != "${BUILD_DIR}/$@" ]; then \
+ mkdir -p ${DEST} && cp ${BUILD_DIR}/$@/*.wgt ${DEST}; \
+ fi
+
+install: build
+ @make -C ${BUILD_DIR} ${INSTALL_ARGS} install
diff --git a/autobuild/linux/autobuild b/autobuild/linux/autobuild
new file mode 100644
index 0000000..569d692
--- /dev/null
+++ b/autobuild/linux/autobuild
@@ -0,0 +1,62 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015 - 2018 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+THISFILE := $(lastword $(MAKEFILE_LIST))
+BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build)
+DEST := ${BUILD_DIR}
+
+.PHONY: all clean distclean configure build package help update
+
+all: help
+
+help:
+ @echo "List of targets available:"
+ @echo ""
+ @echo "- all
+ @echo "- clean"
+ @echo "- distclean"
+ @echo "- configure"
+ @echo "- build: compilation, link and prepare files for package into a widget"
+ @echo "- package: output a widget file '*.wgt'"
+ @echo "- install: install in your defined DEST directory"
+ @echo ""
+ @echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt"
+ @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+ clean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean
+
+distclean:
+ @[ -d ${DEST} ] && find ${DEST} -name "*.wgt" -delete
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} distclean) || echo Nothing to distclean
+
+configure:
+ @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+ @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && qmake ${CONFIGURE_ARGS} ..)
+
+build: configure
+ @make -C ${BUILD_DIR} ${BUILD_ARGS} all
+
+package: build
+ @if [ "${DEST}" != "${BUILD_DIR}/$@" ]; then \
+ mkdir -p ${DEST} && cp ${BUILD_DIR}/$@/*.wgt ${DEST}; \
+ fi
+
+install: build
+ @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
+ mkdir -p ${DEST} && cp -rf ${BUILD_DIR}/package/root/* ${DEST}; \
+ fi
+
diff --git a/log-utils.pro b/log-utils.pro
new file mode 100644
index 0000000..74fe35b
--- /dev/null
+++ b/log-utils.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS = app videoutils tsutils package
+package.depends += app
diff --git a/package/config.xml b/package/config.xml
new file mode 100644
index 0000000..1d61507
--- /dev/null
+++ b/package/config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" id="log-utils" version="0.1">
+ <name>Log-Utils</name>
+ <icon src="icon.svg"/>
+ <content src="bin/log-utils" type="application/vnd.agl.native"/>
+ <description>This is the log application</description>
+ <author>Toyota</author>
+ <license>APL 2.0</license>
+ <feature name="urn:AGL:widget:required-api">
+ <param name="windowmanager" value="ws" />
+ <param name="homescreen" value="ws" />
+ </feature>
+ <feature name="urn:AGL:widget:required-permission">
+ <param name="urn:AGL:permission::public:no-htdocs" value="required" />
+ </feature>
+</widget>
+
+
diff --git a/package/icon.svg b/package/icon.svg
new file mode 100644
index 0000000..6628784
--- /dev/null
+++ b/package/icon.svg
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:i="&amp;ns_ai;"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 320 320"
+ style="enable-background:new 0 0 320 320;"
+ xml:space="preserve"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="icon.svg"><metadata
+ id="metadata1292"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs1290" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1464"
+ id="namedview1288"
+ showgrid="false"
+ inkscape:zoom="0.7375"
+ inkscape:cx="-697.62712"
+ inkscape:cy="160"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><style
+ type="text/css"
+ id="style4">
+ .st0{display:none;}
+ .st1{display:inline;}
+ .st2{opacity:0.4;fill:url(#SVGID_1_);}
+ .st3{fill:url(#SVGID_2_);}
+ .st4{fill:#FFFFFF;}
+ .st5{font-family:'Roboto-Regular';}
+ .st6{font-size:25px;}
+ .st7{letter-spacing:6;}
+ .st8{fill:url(#SVGID_3_);}
+ .st9{fill:url(#SVGID_4_);}
+ .st10{fill:url(#SVGID_5_);}
+ .st11{fill:url(#SVGID_6_);}
+ .st12{fill:url(#SVGID_7_);}
+ .st13{fill:url(#SVGID_8_);}
+ .st14{fill:url(#SVGID_9_);}
+ .st15{fill:url(#SVGID_10_);}
+ .st16{fill:url(#SVGID_11_);}
+ .st17{fill:url(#SVGID_12_);}
+ .st18{fill:url(#SVGID_13_);}
+ .st19{fill:url(#SVGID_14_);}
+ .st20{fill:url(#SVGID_15_);}
+ .st21{fill:url(#SVGID_16_);}
+ .st22{fill:url(#SVGID_17_);}
+ .st23{fill:url(#SVGID_18_);}
+ .st24{opacity:0.29;}
+ .st25{fill:url(#SVGID_19_);}
+ .st26{fill:url(#SVGID_20_);}
+ .st27{fill:url(#SVGID_21_);}
+ .st28{fill:url(#SVGID_22_);}
+ .st29{fill:url(#SVGID_23_);}
+ .st30{fill:url(#SVGID_24_);}
+ .st31{fill:url(#SVGID_25_);}
+ .st32{fill:url(#SVGID_26_);}
+ .st33{fill:url(#SVGID_27_);}
+ .st34{fill:url(#SVGID_28_);}
+ .st35{fill:url(#SVGID_29_);}
+ .st36{fill:url(#SVGID_30_);}
+ .st37{fill:url(#SVGID_31_);}
+ .st38{fill:url(#SVGID_32_);}
+ .st39{fill:url(#SVGID_33_);}
+ .st40{fill:url(#SVGID_34_);}
+ .st41{fill:url(#SVGID_35_);}
+ .st42{fill:url(#SVGID_36_);}
+ .st43{opacity:0.4;fill:url(#SVGID_37_);}
+ .st44{fill:url(#SVGID_38_);}
+ .st45{fill:url(#SVGID_39_);}
+ .st46{fill:url(#SVGID_40_);}
+ .st47{fill:url(#SVGID_41_);}
+ .st48{fill:url(#SVGID_42_);}
+ .st49{fill:url(#SVGID_43_);}
+ .st50{fill:url(#SVGID_44_);}
+ .st51{display:inline;opacity:0.29;}
+ .st52{display:inline;fill:url(#SVGID_45_);}
+ .st53{display:inline;fill:url(#SVGID_46_);}
+ .st54{display:inline;fill:#FFFFFF;}
+ .st55{display:inline;fill:url(#SVGID_47_);}
+ .st56{display:inline;fill:url(#SVGID_48_);}
+ .st57{display:inline;fill:url(#SVGID_49_);}
+ .st58{display:inline;fill:url(#SVGID_50_);}
+ .st59{display:inline;fill:url(#SVGID_51_);}
+ .st60{display:inline;fill:url(#SVGID_52_);}
+ .st61{opacity:0.4;fill:url(#SVGID_53_);}
+ .st62{fill:url(#SVGID_54_);}
+ .st63{fill:url(#SVGID_55_);}
+ .st64{fill:url(#SVGID_56_);}
+ .st65{fill:url(#SVGID_57_);}
+ .st66{fill:url(#SVGID_58_);}
+ .st67{opacity:0.4;fill:url(#SVGID_59_);}
+ .st68{fill:url(#SVGID_60_);}
+ .st69{fill:url(#SVGID_61_);}
+ .st70{fill:url(#SVGID_62_);}
+ .st71{fill:url(#SVGID_63_);}
+ .st72{fill:url(#SVGID_64_);}
+ .st73{fill:url(#SVGID_65_);}
+ .st74{fill:url(#SVGID_66_);}
+ .st75{fill:url(#SVGID_67_);}
+ .st76{fill:url(#SVGID_68_);}
+ .st77{fill:url(#SVGID_69_);}
+ .st78{fill:url(#SVGID_70_);}
+ .st79{fill:url(#SVGID_71_);}
+ .st80{fill:url(#SVGID_72_);}
+ .st81{fill:url(#SVGID_73_);}
+ .st82{fill:url(#SVGID_74_);}
+ .st83{fill:url(#SVGID_75_);}
+ .st84{fill:url(#SVGID_76_);}
+ .st85{fill:url(#SVGID_77_);}
+ .st86{fill:url(#SVGID_78_);}
+ .st87{fill:url(#SVGID_79_);}
+ .st88{fill:url(#SVGID_80_);}
+ .st89{fill:url(#SVGID_81_);}
+ .st90{fill:url(#SVGID_82_);}
+ .st91{fill:url(#SVGID_83_);}
+ .st92{fill:url(#SVGID_84_);}
+ .st93{fill:url(#SVGID_85_);}
+ .st94{fill:url(#SVGID_86_);}
+ .st95{opacity:0.4;fill:url(#SVGID_87_);}
+ .st96{fill:url(#SVGID_88_);}
+ .st97{fill:url(#SVGID_89_);}
+ .st98{fill:url(#SVGID_90_);}
+ .st99{display:inline;fill:url(#SVGID_91_);}
+ .st100{display:inline;fill:url(#SVGID_92_);}
+ .st101{fill:url(#SVGID_93_);}
+ .st102{fill:url(#SVGID_94_);}
+ .st103{opacity:0.4;fill:url(#SVGID_95_);}
+ .st104{fill:url(#SVGID_96_);}
+ .st105{fill:url(#SVGID_97_);}
+ .st106{fill:url(#SVGID_98_);}
+ .st107{fill:url(#SVGID_99_);}
+ .st108{fill:url(#SVGID_100_);}
+ .st109{fill:url(#SVGID_101_);}
+ .st110{display:inline;fill:url(#SVGID_102_);}
+ .st111{display:inline;fill:url(#SVGID_103_);}
+ .st112{fill:url(#SVGID_104_);}
+ .st113{fill:url(#SVGID_105_);}
+ .st114{fill:url(#SVGID_106_);}
+ .st115{fill:url(#SVGID_107_);}
+ .st116{fill:url(#SVGID_108_);}
+ .st117{opacity:0.4;fill:url(#SVGID_109_);}
+ .st118{fill:url(#SVGID_110_);}
+ .st119{fill:url(#SVGID_111_);}
+ .st120{fill:url(#SVGID_112_);}
+ .st121{fill:url(#SVGID_113_);}
+ .st122{fill:url(#SVGID_114_);}
+ .st123{opacity:0.4;fill:url(#SVGID_115_);}
+ .st124{fill:url(#SVGID_116_);}
+ .st125{fill:url(#SVGID_117_);}
+ .st126{fill:url(#SVGID_118_);}
+ .st127{fill:url(#SVGID_119_);}
+ .st128{fill:url(#SVGID_120_);}
+ .st129{fill:url(#SVGID_121_);}
+ .st130{fill:url(#SVGID_122_);}
+</style><switch
+ id="switch6"><g
+ i:extraneous="self"
+ id="g8"><g
+ id="Settings_Active"><circle
+ class="st24"
+ cx="159.7"
+ cy="133.4"
+ r="101.9"
+ id="circle1230" /><linearGradient
+ id="SVGID_119_"
+ gradientUnits="userSpaceOnUse"
+ x1="115.9317"
+ y1="254.1836"
+ x2="256.3852"
+ y2="-133.5267"><stop
+ offset="0"
+ style="stop-color:#8BC53F"
+ id="stop1233" /><stop
+ offset="2.015080e-02"
+ style="stop-color:#7CCB56;stop-opacity:0.9678"
+ id="stop1235" /><stop
+ offset="6.089833e-02"
+ style="stop-color:#62D67D;stop-opacity:0.9028"
+ id="stop1237" /><stop
+ offset="0.1057"
+ style="stop-color:#4BDFA0;stop-opacity:0.8312"
+ id="stop1239" /><stop
+ offset="0.1543"
+ style="stop-color:#38E7BE;stop-opacity:0.7537"
+ id="stop1241" /><stop
+ offset="0.2077"
+ style="stop-color:#28EED6;stop-opacity:0.6684"
+ id="stop1243" /><stop
+ offset="0.2681"
+ style="stop-color:#1CF3E8;stop-opacity:0.572"
+ id="stop1245" /><stop
+ offset="0.3394"
+ style="stop-color:#13F6F5;stop-opacity:0.4581"
+ id="stop1247" /><stop
+ offset="0.4323"
+ style="stop-color:#0EF8FD;stop-opacity:0.3098"
+ id="stop1249" /><stop
+ offset="0.6264"
+ style="stop-color:#0DF9FF;stop-opacity:0"
+ id="stop1251" /></linearGradient><circle
+ class="st127"
+ cx="159.7"
+ cy="133.4"
+ r="101.9"
+ id="circle1253" /><linearGradient
+ id="SVGID_120_"
+ gradientUnits="userSpaceOnUse"
+ x1="4.0481"
+ y1="287.9492"
+ x2="320.4859"
+ y2="-15.4029"
+ gradientTransform="matrix(1 5.464556e-03 -5.464556e-03 1 -2.0192 -3.0212)"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop1256" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop1258" /></linearGradient><path
+ class="st128"
+ d="M160,238.8c-0.2,0-0.4,0-0.6,0c-58-0.3-104.9-47.7-104.6-105.7C55.2,75.3,102.3,28.5,160,28.5 c0.2,0,0.4,0,0.6,0c58,0.3,104.9,47.7,104.6,105.7l0,0C264.8,192,217.7,238.8,160,238.8z M160,32.2 c-55.7,0-101.2,45.2-101.5,100.9c-0.3,55.9,45,101.7,100.9,102c0.2,0,0.4,0,0.6,0c55.7,0,101.2-45.2,101.5-100.9 c0.3-55.9-45-101.7-100.9-102C160.4,32.2,160.2,32.2,160,32.2z"
+ id="path1260" /><g
+ id="g1262"><text
+ transform="matrix(1 0 0 1 75.4379 284.7129)"
+ class="st4 st5 st6 st7"
+ id="text1264">SETTINGS</text>
+<g
+ id="g1266"><g
+ id="g1268"><g
+ id="g1270"><linearGradient
+ id="SVGID_121_"
+ gradientUnits="userSpaceOnUse"
+ x1="79.1804"
+ y1="226.0817"
+ x2="282.752"
+ y2="-4.8609"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop1273" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop1275" /></linearGradient><path
+ class="st129"
+ d="M159.9,163.9c-16.3,0-29.5-13.2-29.5-29.4s13.2-29.4,29.5-29.4v3.9c-14.1,0-25.5,11.4-25.5,25.5 c0,14,11.5,25.5,25.5,25.5c14.1,0,25.6-11.4,25.6-25.5h3.9C189.4,150.7,176.2,163.9,159.9,163.9z"
+ id="path1277" /></g><g
+ id="g1279"><linearGradient
+ id="SVGID_122_"
+ gradientUnits="userSpaceOnUse"
+ x1="79.2457"
+ y1="226.1393"
+ x2="282.8174"
+ y2="-4.8033"><stop
+ offset="0"
+ style="stop-color:#59FF7F"
+ id="stop1282" /><stop
+ offset="1"
+ style="stop-color:#6BFBFF"
+ id="stop1284" /></linearGradient><path
+ class="st130"
+ d="M171.7,197.4h-23.4c-2.2,0-4-1.8-4-3.9V181c-2-0.7-4-1.5-6-2.5l-8.8,8.8c-1.5,1.5-4,1.5-5.6-0.1 l-16.6-16.6c-1.6-1.6-1.6-4.1-0.1-5.6l8.7-8.7c-1-2-1.8-4-2.5-6.1h-12.3c-2.2,0-3.9-1.8-3.9-4v-23.4c0-2.2,1.8-4,3.9-4h12.3 c0.9-2.6,1.9-5.1,3.2-7.4l3.5,1.8c-1.4,2.6-2.5,5.3-3.4,8.1l-0.4,1.4h-15.2l0,23.5l15.2,0.1l0.4,1.4c0.9,2.8,2,5.5,3.4,8 l0.7,1.3L110,167.8l16.6,16.6l10.9-10.8l1.3,0.7c2.6,1.4,5.2,2.5,8,3.3l1.4,0.4v15.4l23.5,0l0.1-15.4l1.4-0.4 c2.7-0.8,5.4-1.9,7.9-3.3l1.3-0.7l10.9,10.9l16.6-16.6l-10.8-11l0.7-1.3c1.4-2.6,2.5-5.2,3.3-7.9l0.4-1.4h15.4l0-23.5 l-15.3-0.1l-0.4-1.4c-0.8-2.8-1.9-5.5-3.3-8l-0.7-1.3l10.8-10.8l-16.6-16.6l-10.8,10.7l-1.3-0.7c-2.6-1.4-5.3-2.5-8.1-3.4 l-1.4-0.4V75.6l-23.5,0l-0.1,15.1l-1.4,0.4c-2.8,0.9-5.6,2-8.1,3.4l-1.3,0.7l-10.7-10.7L107.2,104c-1.5-1.5-1.5-4,0.1-5.6 l16.5-16.5c0.8-0.8,1.8-1.3,2.9-1.2c1,0,2,0.4,2.7,1.1l8.7,8.6c2-1,4-1.8,6.2-2.5V75.6c0-2.2,1.8-3.9,4-3.9h23.4 c2.2,0,4,1.8,4,3.9v12.3c2.1,0.7,4.1,1.6,6.1,2.5l8.7-8.7c0.7-0.7,1.7-1.1,2.7-1.1h0c1.1,0,2.1,0.4,2.9,1.2l16.6,16.6 c0.8,0.8,1.2,1.8,1.2,2.9c0,1-0.4,2-1.1,2.7l-8.8,8.8c1,2,1.8,4,2.5,6h12.4c2.2,0,3.9,1.8,3.9,4v23.4c0,2.2-1.8,4-3.9,4 h-12.5c-0.7,2-1.5,4-2.5,6l8.9,8.9c1.5,1.5,1.5,4-0.1,5.6l-16.6,16.6c-0.8,0.8-1.8,1.2-2.9,1.2h0c-1,0-2-0.4-2.7-1.1 l-8.9-8.9c-1.9,1-3.9,1.8-5.9,2.5v12.5C175.7,195.6,173.9,197.4,171.7,197.4z"
+ id="path1286" /></g></g></g></g></g></g></switch></svg> \ No newline at end of file
diff --git a/package/package.pro b/package/package.pro
new file mode 100644
index 0000000..255e2d4
--- /dev/null
+++ b/package/package.pro
@@ -0,0 +1,21 @@
+
+DISTFILES = icon.svg config.xml
+
+!equals($$_PRO_FILE_PWD_, $$OUT_PWD) {
+ copy_icon.target = $$OUT_PWD/root/icon.svg
+ copy_icon.depends = $$_PRO_FILE_PWD_/icon.svg
+ copy_icon.commands = $(COPY_FILE) \"$$replace(copy_icon.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_icon.target, /, $$QMAKE_DIR_SEP)\"
+ QMAKE_EXTRA_TARGETS += copy_icon
+ PRE_TARGETDEPS += $$copy_icon.target
+
+ copy_config.target = $$OUT_PWD/root/config.xml
+ copy_config.depends = $$_PRO_FILE_PWD_/config.xml
+ copy_config.commands = $(COPY_FILE) \"$$replace(copy_config.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_config.target, /, $$QMAKE_DIR_SEP)\"
+ QMAKE_EXTRA_TARGETS += copy_config
+ PRE_TARGETDEPS += $$copy_config.target
+}
+
+wgt.target = package
+wgt.commands = wgtpkg-pack -f -o log-utils.wgt root
+
+QMAKE_EXTRA_TARGETS += wgt
diff --git a/tsutils/tsplayer/tsplayer.c b/tsutils/tsplayer/tsplayer.c
new file mode 100644
index 0000000..f0ab6f7
--- /dev/null
+++ b/tsutils/tsplayer/tsplayer.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <linux/input.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+typedef struct List
+{
+ int operIndex;
+ int seconds;
+ int microseconds;
+ uint16_t event_Type;
+ uint16_t event_Code;
+ float event_Value;
+ struct List *nextGp;
+}List;
+
+List *eventListHeader;
+List *eventListLast1;
+List *eventListLast2;
+List *eventListLast3;
+
+int touchScreenFd;
+
+extern char* getTouchScreenInfo();
+
+void deleteEventList() {
+ List *prevItem = eventListHeader;
+ List *nextItem = prevItem->nextGp;
+ while(prevItem && nextItem) {
+ free(prevItem);
+ prevItem = nextItem;
+ nextItem = nextItem->nextGp;
+ }
+
+ if (prevItem) {
+ free(prevItem);
+ }
+}
+
+void initEventList()
+{
+ eventListHeader = (struct List*)malloc(sizeof(struct List));
+
+ if (!eventListHeader)
+ {
+ printf("init event list failed. Exit\n");
+ exit(0);
+ }
+ else
+ {
+ memset(eventListHeader,0,sizeof(struct List));
+ eventListHeader->nextGp = NULL;
+ }
+
+ eventListLast1 = NULL;
+ eventListLast2 = NULL;
+ eventListLast3 = NULL;
+}
+
+void checkTouchUp(List *temp)
+{
+ if((temp->event_Type == 3) && (temp->event_Code == 57) && (temp->event_Value == -1))
+ {
+ if(eventListLast1 == NULL)
+ {
+ eventListLast1 = temp;
+ }
+ else if(eventListLast2 == NULL)
+ {
+ eventListLast2 = eventListLast1;
+ eventListLast1 = temp;
+ }
+ else
+ {
+ eventListLast3 = eventListLast2;
+ eventListLast2 = eventListLast1;
+ eventListLast1 = temp;
+ }
+ }
+}
+
+void deleteRedundantData()
+{
+ List *prevItem = eventListLast3->nextGp->nextGp->nextGp;
+ List *nextItem = prevItem->nextGp;
+ while(prevItem && nextItem) {
+ free(prevItem);
+ prevItem = nextItem;
+ nextItem = nextItem->nextGp;
+ }
+
+ if (prevItem) {
+ free(prevItem);
+ }
+}
+
+bool tailCreatList(char *fname)
+{
+ List *tmpData;
+ List *tail;
+
+ char buffer[512];
+
+ FILE *inFile;
+ inFile = fopen(fname,"r");
+
+ if (inFile == NULL)
+ {
+ printf("\nFailed to open the file : %s . Exit\n",fname);
+ exit(0);
+ }
+
+ initEventList();
+ tail = eventListHeader;
+
+ memset(buffer,0,sizeof(buffer));
+
+ while(fgets(buffer,sizeof(buffer),inFile))
+ {
+ tmpData = (struct List*)malloc(sizeof(struct List));
+ if (!tmpData)
+ {
+ printf("Faild to create event list. Exit\n");
+ fclose(inFile);
+ exit(0);
+ }
+
+ memset(tmpData,0,sizeof(struct List));
+
+ tmpData->nextGp = NULL;
+ sscanf(buffer,"%d %d %d %hu %hu %f",&(tmpData->operIndex),&(tmpData->seconds),&(tmpData->microseconds),&(tmpData->event_Type),&(tmpData->event_Code),&(tmpData->event_Value));
+
+ checkTouchUp(tmpData);
+ tail->nextGp = tmpData;
+ tail = tmpData;
+
+ memset(buffer,0,sizeof(buffer));
+ }
+
+ if(eventListLast3 != NULL)
+ {
+ tail = eventListLast3->nextGp->nextGp;
+ deleteRedundantData();
+ }
+ else
+ {
+ deleteEventList();
+ return false;
+ }
+
+ tail->nextGp = NULL;
+
+ fclose(inFile);
+
+ return true;
+}
+
+int reportKey(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+ struct input_event event;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+
+ gettimeofday(&event.time, 0);
+
+ if (write(fd, &event, sizeof(struct input_event)) < 0) {
+ printf("report key error!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void touchEventTest(List *listHeader, int screenWidth, int screenHeight,
+ int delayTime)
+{
+ List *p = listHeader->nextGp;
+ printf("--------Touch Event Test Start!!!--------\n");
+
+ while(p != NULL)
+ {
+ if ((p->event_Type == 3) && ((p->event_Code == 53) || (p->event_Code == 0)))
+ {
+ reportKey(touchScreenFd,p->event_Type,p->event_Code,p->event_Value * screenWidth);
+ }
+ else if ((p->event_Type == 3) && ((p->event_Code == 54) || (p->event_Code == 1)))
+ {
+ reportKey(touchScreenFd,p->event_Type,p->event_Code,p->event_Value * screenHeight);
+ }
+ else
+ {
+ reportKey(touchScreenFd,p->event_Type,p->event_Code,p->event_Value);
+ }
+
+ if (p->nextGp != NULL)
+ {
+ usleep((p->nextGp->seconds - p->seconds) * 1000000 + (p->nextGp->microseconds - p->microseconds));
+ }
+
+ p = p->nextGp;
+
+ }
+ printf("--------Touch Event Test End!!!--------\n");
+}
+
+int appNeedEventTestStart(int tsWidth, int tsHeight, char* eventNode,
+ char* eventFile, int delayTime)
+{
+ touchScreenFd = open(eventNode, O_RDWR);
+
+ if (touchScreenFd < 0)
+ {
+ printf("Open %s failed.\n", eventNode);
+ return -1;
+ }
+
+ if(tailCreatList(eventFile))
+ {
+ touchEventTest(eventListHeader, tsWidth, tsHeight, delayTime);
+ deleteEventList();
+ }
+
+ close(touchScreenFd);
+
+ return 1;
+}
+
+void printUsage(char* myName)
+{
+ printf("usage:\n \
+ %s -a <string> -d <number> -c <string> -e <number> -f <string>\n \
+ -a : application name(must)\n \
+ -d : the time interval(> 0) for capturing logs(unit:ms)(must).\n \
+ -c : child process name(s).If more than one, sperated by ','. \n\t\tEg. -c process1,prcocess2,process3.\n \
+ -f : file name for recorded event. If -e is 1, then -f must be specified.\n \
+ -e : 1 for apps that need events, 0 for apps that does not need events.Default is 0. \n", myName);
+}
+
+int main(int argc,char *argv[])
+{
+ int index = 0;
+ int screenWidth, screenHeight;
+ char eventFileName[100];
+ char eventNode[50];
+ const char* tsInfo;
+ int needEvents = 0;
+ int delayTime = 0;
+
+ if (argc < 5)
+ {
+ printUsage(argv[0]);
+ return -1;
+ }
+
+ memset(eventFileName, 0, sizeof(eventFileName));
+ memset(eventNode, 0, sizeof(eventNode));
+
+ for (index = 1; index < argc; index ++)
+ {
+ if (strcmp(argv[index], "-f") == 0)
+ {
+ memcpy(eventFileName, argv[++index], strlen(argv[index]));
+ }
+ else if (strcmp(argv[index], "-d") == 0)
+ {
+ delayTime = atoi(argv[++index]);
+ }
+ else if (strcmp(argv[index], "-e") == 0)
+ {
+ needEvents = atoi(argv[++index]);
+ }
+ else
+ {
+ printUsage(argv[0]);
+ return -1;
+ }
+ }
+
+ if (0 == delayTime) {
+ printf("Please specify an interval time(> 0)\n");
+ printUsage(argv[0]);
+ return -1;
+ }
+
+ printf("step %d. \n", __LINE__);
+ if ((1 == needEvents) && (0 == strlen(eventFileName))) {
+ printf("You should specify a file for recorded event.\n");
+ printUsage(argv[0]);
+ return -1;
+ }
+ printf("step %d. \n", __LINE__);
+
+ if (1 == needEvents)
+ {
+ printf("step %d. \n", __LINE__);
+ tsInfo = getTouchScreenInfo();
+ printf("step %d. \n", __LINE__);
+ if (NULL == tsInfo) {
+ printf("Failed to get touch screen info.\n");
+ return -1;
+ }
+ printf("step %d. tsInfo = %s. \n", __LINE__, tsInfo);
+
+ sscanf(tsInfo, "%s %d %d", eventNode, &screenWidth, &screenHeight);
+ appNeedEventTestStart(screenWidth, screenHeight, eventNode, eventFileName,
+ delayTime);
+ printf("step %d. \n", __LINE__);
+
+ }
+ return 0;
+}
diff --git a/tsutils/tsplayer/tsplayer.pro b/tsutils/tsplayer/tsplayer.pro
new file mode 100644
index 0000000..8ff7cce
--- /dev/null
+++ b/tsutils/tsplayer/tsplayer.pro
@@ -0,0 +1,7 @@
+TARGET = tsplayer
+
+DESTDIR = $${OUT_PWD}/../../package/root/bin
+
+SOURCES += \
+ tsplayer.c \
+ ../tsutils.c
diff --git a/tsutils/tsrecorder/tsrecorder.c b/tsutils/tsrecorder/tsrecorder.c
new file mode 100644
index 0000000..9c2e466
--- /dev/null
+++ b/tsutils/tsrecorder/tsrecorder.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <linux/input.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+
+static int eventFd = -1;
+static FILE* pfile;
+struct input_event ev0[64];
+static int screenWidth = 0;
+static int screenHeight = 0;
+
+extern char* getTouchScreenInfo();
+
+static int handle_event()
+{
+ int i, rd;
+ char str[256];
+ float x = 0, y = 0;
+
+ rd = read(eventFd, ev0, sizeof(struct input_event)* 64);
+
+ if(rd < sizeof(struct input_event))
+ return 0;
+
+ for(i=0;i<rd/sizeof(struct input_event); i++)
+ {
+ memset(str, 0, sizeof(str));
+
+ if(ev0[i].type == 3)
+ {
+ if(ev0[i].code == 53 || ev0[i].code == 0)
+ {//screen widthに対して変換
+ x = (float)ev0[i].value / screenWidth;
+ sprintf(str, "%2d\t%ld\t%ld\t%3hu\t%3hu\t%f\r\n",
+ i, ev0[i].time.tv_sec, ev0[i].time.tv_usec, ev0[i].type, ev0[i].code, x);
+ }
+ else if(ev0[i].code == 54 || ev0[i].code == 1)
+ {//screen heightに対して変換
+ y = (float)ev0[i].value / screenHeight;
+ sprintf(str, "%2d\t%ld\t%ld\t%3hu\t%3hu\t%f\r\n",
+ i, ev0[i].time.tv_sec, ev0[i].time.tv_usec, ev0[i].type, ev0[i].code, y);
+ }
+ else
+ {
+ sprintf(str, "%2d\t%ld\t%ld\t%3hu\t%3hu\t%d\r\n", i, ev0[i].time.tv_sec, ev0[i].time.tv_usec, ev0[i].type, ev0[i].code, ev0[i].value);
+ }
+ }
+ else
+ {
+ sprintf(str, "%2d\t%ld\t%ld\t%3hu\t%3hu\t%d\r\n", i, ev0[i].time.tv_sec, ev0[i].time.tv_usec, ev0[i].type, ev0[i].code, ev0[i].value);
+ }
+
+ fputs(str, pfile);
+ }
+ fflush(pfile);
+ return 1;
+}
+
+void printUsage(char* myName)
+{
+ printf("usage:\n \
+ %s -f <string>\n \
+ -f : file name for recording event\n", myName);
+}
+
+int main(int argc,char *argv[])
+{
+ int done = 1;
+ int index = 0;
+ char* fName = NULL;
+ char eventNode[50];
+ const char* tsInfo;
+
+ if (argc < 3)
+ {
+ printUsage(argv[0]);
+ return -1;
+ }
+
+ for (index = 1; index < argc; index++)
+ {
+ if (strcmp(argv[index], "-f") == 0)
+ {
+ fName = argv[++index];
+ }
+ else
+ {
+ printUsage(argv[0]);
+ return -1;
+ }
+ }
+
+ tsInfo = getTouchScreenInfo();
+ if (NULL == tsInfo) {
+ printf("Failed to get touch screen info.\n");
+ return -1;
+ }
+
+ memset(eventNode, 0, sizeof(eventNode));
+ sscanf(tsInfo, "%s %d %d", eventNode, &screenWidth, &screenHeight);
+
+ eventFd = open(eventNode, O_RDWR);
+ if(eventFd <0) {
+ printf("open input device error\n");
+ return -1;
+ }
+
+ pfile = fopen(fName, "wb+");
+ if(pfile == NULL)
+ {
+ printf("Failed to create file!\n");
+ return -1;
+ }
+
+ while (done)
+ {
+ printf("begin %s...\n", eventNode);
+ done = handle_event();
+ printf("end %s...\n", eventNode);
+ }
+
+ fclose(pfile);
+
+ if(eventFd > 0)
+ {
+ close(eventFd);
+ eventFd = -1;
+ }
+
+ return 0;
+}
diff --git a/tsutils/tsrecorder/tsrecorder.pro b/tsutils/tsrecorder/tsrecorder.pro
new file mode 100644
index 0000000..3c3988b
--- /dev/null
+++ b/tsutils/tsrecorder/tsrecorder.pro
@@ -0,0 +1,7 @@
+TARGET = tsrecorder
+
+DESTDIR = $${OUT_PWD}/../../package/root/bin
+
+SOURCES += \
+ tsrecorder.c \
+ ../tsutils.c
diff --git a/tsutils/tsutils.c b/tsutils/tsutils.c
new file mode 100644
index 0000000..79385d1
--- /dev/null
+++ b/tsutils/tsutils.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <dirent.h>
+#include <linux/input.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#define LONG_BITS (sizeof(long) << 3)
+#define NUM_LONGS(bits) (((bits) + LONG_BITS - 1) / LONG_BITS)
+#define DEV_INPUT "/dev/input"
+
+char tsInfo[200];
+
+static inline int testBit(long bit, const long *array) {
+ return (array[bit / LONG_BITS] >> bit % LONG_BITS) & 1;
+}
+
+char* getTouchScreenInfo() {
+ DIR* dir;
+ struct dirent* itemPtr;
+ int fd;
+ int isSingleTouch = 0, hasTouchScreen = 0;
+ long absbits[NUM_LONGS(ABS_CNT)];
+ char tsDevNode[100];
+ struct input_absinfo absInfo;
+
+ int maxX, maxY;
+
+ memset(tsDevNode, 0, sizeof(tsDevNode));
+ memset(tsInfo, 0, sizeof(tsInfo));
+
+ if ((dir = opendir(DEV_INPUT)) == NULL) {
+ printf("open %s failed.\n", DEV_INPUT);
+ return NULL;
+ }
+
+ while ((itemPtr = readdir(dir)) != NULL) {
+ //printf("name : %s---type : %d\n", itemPtr->d_name, itemPtr->d_type);
+ if ((strstr(itemPtr->d_name, "event") != NULL) && (2 == itemPtr->d_type)) {
+ sprintf(tsDevNode, "%s/%s", DEV_INPUT, itemPtr->d_name);
+ fd = open(tsDevNode, O_RDONLY);
+ if (fd < 0) {
+ printf("open %s failed.\n", tsDevNode);
+ return NULL;
+ }
+
+ if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) >= 0) {
+ isSingleTouch = !testBit(ABS_MT_POSITION_X, absbits);
+ } else {
+ close(fd);
+ continue;
+ }
+
+ if (ioctl(fd, EVIOCGABS((isSingleTouch ? ABS_X : ABS_MT_POSITION_X)), &absInfo) >= 0) {
+ maxX = absInfo.maximum;
+ } else {
+ close(fd);
+ continue;
+ }
+
+ if (ioctl(fd, EVIOCGABS((isSingleTouch ? ABS_Y : ABS_MT_POSITION_Y)), &absInfo) >= 0) {
+ maxY = absInfo.maximum;
+ } else {
+ close(fd);
+ continue;
+ }
+
+ hasTouchScreen = 1;
+ break;
+ }
+ }
+
+ if (!hasTouchScreen) return NULL;
+
+ sprintf(tsInfo, "%s %d %d", tsDevNode, maxX, maxY);
+
+ close(fd);
+ closedir(dir);
+ return tsInfo;
+}
diff --git a/tsutils/tsutils.pro b/tsutils/tsutils.pro
new file mode 100644
index 0000000..ed02c62
--- /dev/null
+++ b/tsutils/tsutils.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS = tsplayer tsrecorder \ No newline at end of file
diff --git a/videoutils/videoutils.cpp b/videoutils/videoutils.cpp
new file mode 100644
index 0000000..343469c
--- /dev/null
+++ b/videoutils/videoutils.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "videoutils.h"
+#include <opencv2/core.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+
+#include <iostream>
+#include <unistd.h>
+#include <stdlib.h>
+#include <boost/algorithm/string.hpp>
+#include <glib.h>
+#include <sys/time.h>
+
+using namespace VIDEO_UTILS;
+
+GMainLoop * mainloop = NULL;
+static int grabImage(void *data);
+static void printhelp(const char *argv0);
+static void mainLoopQuit(int sig);
+
+VideoUtils::VideoUtils(int device, int framerate, int wid, int hght, std::string file, std::string co)
+ : fps(framerate), width(wid), height(hght), deviceno(device), codec(co), filename(file)
+{
+ cvCapture = std::unique_ptr < cv::VideoCapture > (new cv::VideoCapture());
+ cvWriter = std::unique_ptr < cv::VideoWriter > (new cv::VideoWriter());
+
+ isReady = false;
+ framecount = 0;
+
+ if (cvCapture && !cvCapture->isOpened())
+ {
+ if (cvCapture->open(deviceno))
+ {
+ cvCapture->set(cv::CAP_PROP_FRAME_WIDTH, width);
+ cvCapture->set(cv::CAP_PROP_FRAME_HEIGHT, height);
+
+ if (cvWriter && !cvWriter->isOpened())
+ {
+ isReady = openVideoWriter();
+ }
+ }
+ }
+}
+
+void VideoUtils::releaseSource()
+{
+ if (cvCapture && cvCapture->isOpened())
+ {
+ cvCapture->release();
+ }
+
+ if (cvWriter && cvWriter->isOpened())
+ {
+ cvWriter->release();
+ }
+}
+
+bool VideoUtils::openVideoWriter()
+{
+ if (!cvCapture || !cvCapture->isOpened() || !cvWriter)
+ {
+ return false;
+ }
+
+ /* Get the size of a frame from the device. */
+ cv::Size framesize = cv::Size((int) cvCapture->get(cv::CAP_PROP_FRAME_WIDTH), (int) cvCapture->get(cv::CAP_PROP_FRAME_HEIGHT));
+
+ /* Get the CODEC. */
+ if (codec.empty() || codec.size() != 4)
+ {
+ codec = "MJPG";
+ }
+ boost::algorithm::to_upper(codec);
+
+ return cvWriter->open(filename, cv::VideoWriter::fourcc(codec.at(0), codec.at(1), codec.at(2), codec.at(3)), fps, framesize);
+}
+
+void VideoUtils::writeVideoFrame(const cv::UMat& f)
+{
+ if (!cvWriter || !cvWriter->isOpened())
+ {
+ return;
+ }
+
+ if (framecount < fps)
+ {
+ framecount++;
+ return;
+ }
+
+ cv::Mat frame;
+ f.copyTo(frame);
+
+ /* Put the current time into the frame. */
+ int fontFace = cv::FONT_HERSHEY_SIMPLEX;
+ double fontScale = 0.6;
+ int baseline = 0;
+ int thickness = 2;
+ std::stringstream datestr;
+
+ datestr << getCurrentTime();
+
+ cv::Size dateTextSize = cv::getTextSize(datestr.str(), fontFace, fontScale, thickness, &baseline);
+
+ for (; fontScale > 0.1 && dateTextSize.width > frame.cols; )
+ {
+ fontScale -= 0.2;
+ dateTextSize = cv::getTextSize(datestr.str(), fontFace, fontScale, thickness, &baseline);
+ }
+
+ cv::Point dateOrg(frame.cols - dateTextSize.width, dateTextSize.height);
+
+ cv::putText(frame, datestr.str(), dateOrg, fontFace, fontScale, cv::Scalar(255, 255, 255), thickness);
+
+ /* Write the frame into the log file. */
+ *(cvWriter.get()) << frame;
+}
+
+std::string VideoUtils::getCurrentTime()
+{
+ time_t seconds;
+ struct tm currenttime;
+ if (time(&seconds) == (time_t) - 1)
+ {
+ return "";
+ }
+ localtime_r(&seconds, &currenttime);
+
+ char timestr[] = "1900/01/01/00/00/00";
+
+ sprintf(timestr, "%04d/%02d/%02d %02d:%02d:%02d", currenttime.tm_year + 1900, currenttime.tm_mon + 1, currenttime.tm_mday, currenttime.tm_hour, currenttime.tm_min, currenttime.tm_sec);
+
+ return timestr;
+}
+
+static int grabImage(void *data)
+{
+ VideoUtils* utils = reinterpret_cast<VideoUtils*>(data);
+
+ if (!utils->cvCapture || !utils->cvCapture->isOpened())
+ {
+ return false;
+ }
+
+ cv::UMat frame;
+ *(utils->cvCapture.get()) >> frame;
+
+ if (!frame.empty())
+ {
+ /* Write the frame into the log file. */
+ utils->writeVideoFrame(frame);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+static void printhelp(const char *argv0)
+{
+ printf("Usage: %s [args]\n"
+ " [-d|--device <0-5> ] set the device\n"
+ " [-w|--width] specify the width\n"
+ " [-h|--height] specify the height\n"
+ " [-r]--frame rate] specify the frame rate\n"
+ " [-c]--codec] specify the codec\n"
+ " [-f]--folder name] specify a folder to save the video file\n", argv0);
+}
+
+static void mainLoopQuit(int sig)
+{
+ if ((sig == SIGUSR1) && (mainloop != NULL))
+ {
+ g_main_loop_quit(mainloop);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ static int opt;
+ int device = 0;
+ int framerate = 30;
+ int width = 640;
+ int height = 480;
+ std::string filename = "/tmp/temp.avi";
+ std::string codec = "MP42";
+
+ while ((opt = getopt(argc, argv, "d:w:h:r:f:c:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'd':
+ device = atoi(optarg);
+ break;
+ case 'w':
+ width = atoi(optarg);
+ break;
+ case 'h':
+ height = atoi(optarg);
+ break;
+ case 'r':
+ framerate = atoi(optarg);
+ break;
+ case 'f':
+ filename.assign(optarg);
+ break;
+ case 'c':
+ codec.assign(optarg);
+ break;
+ default:
+ printhelp(basename(argv[0]));
+ return (0);
+ }
+ }
+
+ VideoUtils utils(device, framerate, width, height, filename, codec);
+
+ if (utils.isReady && framerate > 0)
+ {
+ void (*oldSigUsr1Handler)(int);
+
+ oldSigUsr1Handler = signal(SIGUSR1, mainLoopQuit);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+
+ g_timeout_add(1000 / framerate, grabImage, &utils);
+
+ g_main_loop_run(mainloop);
+ g_main_loop_unref(mainloop);
+
+ if (oldSigUsr1Handler != SIG_ERR)
+ {
+ signal(SIGUSR1, oldSigUsr1Handler);
+ }
+ }
+
+ utils.releaseSource();
+
+ return 0;
+}
diff --git a/videoutils/videoutils.h b/videoutils/videoutils.h
new file mode 100644
index 0000000..78a0372
--- /dev/null
+++ b/videoutils/videoutils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIDEOUTILS_H
+#define VIDEOUTILS_H
+
+#include <memory>
+#include <string>
+#include <opencv2/videoio.hpp>
+
+namespace VIDEO_UTILS
+{
+class VideoUtils
+{
+public:
+ VideoUtils(int device, int framerate, int wid, int hght, std::string file, std::string co);
+ ~VideoUtils()
+ {
+ }
+ void writeVideoFrame(const cv::UMat& f);
+ void releaseSource();
+
+private:
+ std::string getCurrentTime();
+ bool openVideoWriter();
+
+public:
+ bool isReady;
+ std::unique_ptr<cv::VideoCapture> cvCapture;
+
+private:
+ int fps;
+ int width;
+ int height;
+ int deviceno;
+ int framecount;
+ std::string codec;
+ std::string filename;
+ std::unique_ptr<cv::VideoWriter> cvWriter;
+};
+}
+
+#endif // VIDEOUTILS_H
diff --git a/videoutils/videoutils.pro b/videoutils/videoutils.pro
new file mode 100644
index 0000000..9630cc5
--- /dev/null
+++ b/videoutils/videoutils.pro
@@ -0,0 +1,16 @@
+TARGET = videoutils
+
+LIBS += -lopencv_core -lopencv_videoio -lglib-2.0 -lopencv_imgproc
+
+DESTDIR = $${OUT_PWD}/../package/root/bin
+
+HEADERS += \
+ videoutils.h
+
+SOURCES += \
+ videoutils.cpp
+
+INCLUDEPATH += \
+ $$[QT_SYSROOT]/usr/include/glib-2.0/ \
+ $$[QT_SYSROOT]/usr/lib/glib-2.0/include
+