summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzhou_xin <zhou_xin@dl.cn.nexty-ele.com>2019-03-02 13:41:21 +0800
committerzhou_xin <zhou_xin@dl.cn.nexty-ele.com>2019-03-02 13:41:21 +0800
commitc8788c356e6b0f9d50f639fe39e926eab2586b1c (patch)
treea3f498e3478db1a7e149de3ae686fceb407e3ca6
parent3d46d6a0cf923eeea00052e6ed9cfd0a32965736 (diff)
Add log function to settings
-rw-r--r--app/Settings.qml3
-rw-r--r--app/app.pro13
-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.qml143
-rw-r--r--app/logfile/LogPlay.qml355
-rw-r--r--app/logfile/LogSave.qml238
-rw-r--r--app/logfile/TouchLog.qml13
-rw-r--r--app/logfile/TouchLogPlay.qml173
-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.cpp355
-rw-r--r--app/logfile/logsave.h89
-rw-r--r--app/logfile/touchlogplay.cpp261
-rw-r--r--app/logfile/touchlogplay.h71
-rw-r--r--app/main.cpp8
29 files changed, 3269 insertions, 2 deletions
diff --git a/app/Settings.qml b/app/Settings.qml
index bfcec3a..2040345 100644
--- a/app/Settings.qml
+++ b/app/Settings.qml
@@ -22,6 +22,7 @@ import 'datetime'
import 'bluetooth'
import 'wifi'
import 'version'
+import 'logfile'
ApplicationWindow {
id: root
@@ -69,6 +70,8 @@ ApplicationWindow {
Wifi {}
+ LogFile {}
+
Version {}
}
}
diff --git a/app/app.pro b/app/app.pro
index 566d2b6..c7278b4 100644
--- a/app/app.pro
+++ b/app/app.pro
@@ -1,7 +1,15 @@
TARGET = settings
QT = quickcontrols2 websockets
-SOURCES = main.cpp
+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
@@ -12,7 +20,8 @@ RESOURCES += \
datetime/datetime.qrc \
wifi/wifi.qrc \
bluetooth/bluetooth.qrc \
- version/version.qrc
+ version/version.qrc \
+ logfile/logfile.qrc
include(app.pri)
diff --git a/app/logfile/AudioLog.qml b/app/logfile/AudioLog.qml
new file mode 100644
index 0000000..da2fd27
--- /dev/null
+++ b/app/logfile/AudioLog.qml
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 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..444c107
--- /dev/null
+++ b/app/logfile/Bar.qml
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 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..a3e26a1
--- /dev/null
+++ b/app/logfile/CANLog.qml
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 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..fbf2eb9
--- /dev/null
+++ b/app/logfile/LogFile.qml
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 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'
+
+SettingPage {
+ id: root
+ icon: '/logfile/images/HMI_Settings_LogFile.svg'
+ title: 'LogFile'
+ property string trippath: ''
+ property string rootpath: ''
+ property var targets: new Array()
+
+ Bar {
+ id: tabbar
+ 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..dbe5555
--- /dev/null
+++ b/app/logfile/LogPlay.qml
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2017 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..32491aa
--- /dev/null
+++ b/app/logfile/LogSave.qml
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2017 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..780e186
--- /dev/null
+++ b/app/logfile/TouchLog.qml
@@ -0,0 +1,13 @@
+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..dc12a74
--- /dev/null
+++ b/app/logfile/TouchLogPlay.qml
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2017 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:", playing);
+ if(playing) {
+ touchlogplayimpl.touchPlay(false)
+ }
+ else {
+ touchlogplayimpl.setTouchProperty(touchloglist.currentIndex)
+ touchlogplayimpl.touchPlay(true)
+ }
+ }
+ }
+ }
+
+ TouchLogPlayImpl {
+ id: touchlogplayimpl
+
+ onPropTouchPlayFlagChanged: {
+ console.log("onPropTouchPlayFlagChanged:", touchlogplayimpl.propTouchPlayFlag);
+ if(touchlogplayimpl.propTouchPlayFlag) {
+ playstopbutton.text = "Stop"
+ } else {
+ playstopbutton.text = "Play"
+ }
+ }
+ }
+
+ 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..34105cb
--- /dev/null
+++ b/app/logfile/VideoLog.qml
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2017 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..95a0b06
--- /dev/null
+++ b/app/logfile/keyboard/AbstractKeyboard.qml
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ *
+ * 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..989d108
--- /dev/null
+++ b/app/logfile/keyboard/Key.qml
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ *
+ * 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..0500e30
--- /dev/null
+++ b/app/logfile/keyboard/Keyboard.qml
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ *
+ * 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..1d909b7
--- /dev/null
+++ b/app/logfile/keyboard/Numbers.qml
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ *
+ * 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..53febea
--- /dev/null
+++ b/app/logfile/logplay.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2017 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..02b6430
--- /dev/null
+++ b/app/logfile/logplay.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017 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..3482cba
--- /dev/null
+++ b/app/logfile/logsave.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2017 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>
+
+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 = "/usr/bin/videoutils";
+const std::string LogSaveImpl::TouchRecUtilsExec = "/usr/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)
+ {
+ 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..2972fcd
--- /dev/null
+++ b/app/logfile/logsave.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017 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..db7b1a4
--- /dev/null
+++ b/app/logfile/touchlogplay.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2017 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>
+
+const std::string TouchLogPlayImpl::touchPlayUtilsExec = "/usr/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;
+ }
+
+ 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;
+ emit pointer->propTouchPlayFlagChangedDelegate();
+ qDebug("finalizeChild end.");
+ }
+
+ if(signal(SIGCHLD, oldHandler) == SIG_ERR)
+ {
+ qDebug("signal error.");
+ }
+}
diff --git a/app/logfile/touchlogplay.h b/app/logfile/touchlogplay.h
new file mode 100644
index 0000000..846bc7d
--- /dev/null
+++ b/app/logfile/touchlogplay.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017 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);
+
+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
index 3b39810..58abd30 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -29,6 +29,10 @@
#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("settings"); // defined in layers.json in window manager
@@ -49,6 +53,10 @@ int main(int argc, char *argv[])
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);