提交 5f9e667d 编写于 作者: Y Yan Chunwei 提交者: GitHub

Merge pull request #1 from PaddlePaddle/feature/refactor_im_singleton

#!/bin/bash
set -e
readonly VERSION="3.8"
version=$(clang-format -version)
if ! [[ $version == *"$VERSION"* ]]; then
echo "clang-format version check failed."
echo "a version contains '$VERSION' is needed, but get '$version'"
echo "you can install the right version, and make an soft-link to '\$PATH' env"
exit -1
fi
clang-format $@
......@@ -99,3 +99,5 @@ ENV/
# mypy
.mypy_cache/
.DS_Store
- repo: https://github.com/pre-commit/mirrors-yapf.git
sha: v0.16.0
hooks:
- id: yapf
files: \.py$
- repo: https://github.com/pre-commit/pre-commit-hooks
sha: a11d9314b22d8f8c7556443875b731ef05965464
hooks:
- id: check-merge-conflict
- id: check-symlinks
- id: end-of-file-fixer
- id: trailing-whitespace
- id: detect-private-key
- id: check-symlinks
- id: check-added-large-files
- repo: local
hooks:
- id: clang-format-with-version-check
name: clang-format
description: Format files with ClangFormat.
entry: bash ./.clang_format.hook -i
language: system
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|proto)$
language: cpp
cache:
directories:
- $HOME/.ccache
- $HOME/.cache/pip
sudo: required
dist: trusty
os:
- linux
addons:
apt:
packages:
- gcc-4.8
- g++-4.8
- git
- python
- python-pip
- python2.7-dev
- python-wheel
- clang-format-3.8
- ccache
script:
/bin/bash ./tests.sh
notifications:
email:
on_success: change
on_failure: always
cmake_minimum_required(VERSION 3.2)
project(VisualDL)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "-fPIC")
set(THIRD_PARTY_PATH "${CMAKE_BINARY_DIR}/third_party" CACHE STRING
"A path setting third party libraries download & build directories.")
################################ Configurations #######################################
option(WITH_TESTING "Compile VisualDL with unit testing" ON)
include(external/zlib) # download, build, install zlib
include(external/gflags) # download, build, install gflags
include(external/glog) # download, build, install glog
include(external/gtest) # download, build, install gtest
include(external/pybind11) # download pybind11
include(external/protobuf) # download, build, install protobuf
include(external/python) # find python and set path
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/local/include)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/storage)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/logic)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/python)
add_executable(vl_test
${PROJECT_SOURCE_DIR}/visualdl/test.cc
${PROJECT_SOURCE_DIR}/visualdl/storage/storage_test.cc
${PROJECT_SOURCE_DIR}/visualdl/utils/test_concurrency.cc
${PROJECT_SOURCE_DIR}/visualdl/logic/im_test.cc
${PROJECT_SOURCE_DIR}/visualdl/logic/sdk_test.cc
${PROJECT_SOURCE_DIR}/visualdl/utils/concurrency.h
${PROJECT_SOURCE_DIR}/visualdl/utils/filesystem.h
)
target_link_libraries(vl_test storage sdk im gtest glog protobuf gflags pthread)
enable_testing ()
add_custom_target(test_init COMMAND ./init_test.sh $CMAKE_BINARY_DIR)
add_test(NAME vstest COMMAND ./vl_test)
set_target_properties(vl_test PROPERTIES DEPENDS test_init)
# VisualDL
\ No newline at end of file
# VisualDL
### How to use
#### Step 1: build frontend
```shell
cd frontend
npm install
npm run build
```
this step will generate a dist directory under frontend
### Step 2: copy frontend/dist to server/visualdl/frontend/dist
```shell
mkdir -p server/visualdl/frontend/dist
cp -r frontend/dist server/visualdl/frontend/dist
```
#### Step 3: build and install Python package
```shell
cd server/
python setup.py bdist_wheel
cd dist
sudo pip install --upgrade visualdl-0.0.1-py2-none-any.whl
```
### Step 3: run
```
# cd to visualdl install dir
cd /usr/local/lib/python2.7/site-packages/visualdl/
python visual_dl.py --port=8888
```
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# 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(ExternalProject)
SET(GFLAGS_SOURCES_DIR ${THIRD_PARTY_PATH}/gflags)
SET(GFLAGS_INSTALL_DIR ${THIRD_PARTY_PATH}/install/gflags)
SET(GFLAGS_INCLUDE_DIR "${GFLAGS_INSTALL_DIR}/include" CACHE PATH "gflags include directory." FORCE)
IF(WIN32)
set(GFLAGS_LIBRARIES "${GFLAGS_INSTALL_DIR}/lib/gflags.lib" CACHE FILEPATH "GFLAGS_LIBRARIES" FORCE)
ELSE(WIN32)
set(GFLAGS_LIBRARIES "${GFLAGS_INSTALL_DIR}/lib/libgflags.a" CACHE FILEPATH "GFLAGS_LIBRARIES" FORCE)
ENDIF(WIN32)
INCLUDE_DIRECTORIES(${GFLAGS_INCLUDE_DIR})
ExternalProject_Add(
extern_gflags
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/gflags/gflags.git"
GIT_TAG 77592648e3f3be87d6c7123eb81cbad75f9aef5a
PREFIX ${GFLAGS_SOURCES_DIR}
UPDATE_COMMAND ""
CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
-DCMAKE_INSTALL_PREFIX=${GFLAGS_INSTALL_DIR}
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DBUILD_TESTING=OFF
-DCMAKE_BUILD_TYPE=${THIRD_PARTY_BUILD_TYPE}
${EXTERNAL_OPTIONAL_ARGS}
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${GFLAGS_INSTALL_DIR}
-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=${THIRD_PARTY_BUILD_TYPE}
)
ADD_LIBRARY(gflags STATIC IMPORTED GLOBAL)
SET_PROPERTY(TARGET gflags PROPERTY IMPORTED_LOCATION ${GFLAGS_LIBRARIES})
ADD_DEPENDENCIES(gflags extern_gflags)
LIST(APPEND external_project_dependencies gflags)
IF(WITH_C_API)
INSTALL(DIRECTORY ${GFLAGS_INCLUDE_DIR} DESTINATION third_party/gflags)
IF(ANDROID)
INSTALL(FILES ${GFLAGS_LIBRARIES} DESTINATION third_party/gflags/lib/${ANDROID_ABI})
ELSE()
INSTALL(FILES ${GFLAGS_LIBRARIES} DESTINATION third_party/gflags/lib)
ENDIF()
ENDIF()
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# 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(ExternalProject)
SET(GLOG_SOURCES_DIR ${THIRD_PARTY_PATH}/glog)
SET(GLOG_INSTALL_DIR ${THIRD_PARTY_PATH}/install/glog)
SET(GLOG_INCLUDE_DIR "${GLOG_INSTALL_DIR}/include" CACHE PATH "glog include directory." FORCE)
IF(WIN32)
SET(GLOG_LIBRARIES "${GLOG_INSTALL_DIR}/lib/libglog.lib" CACHE FILEPATH "glog library." FORCE)
ELSE(WIN32)
SET(GLOG_LIBRARIES "${GLOG_INSTALL_DIR}/lib/libglog.a" CACHE FILEPATH "glog library." FORCE)
ENDIF(WIN32)
INCLUDE_DIRECTORIES(${GLOG_INCLUDE_DIR})
ExternalProject_Add(
extern_glog
${EXTERNAL_PROJECT_LOG_ARGS}
DEPENDS gflags
GIT_REPOSITORY "https://github.com/google/glog.git"
GIT_TAG v0.3.5
PREFIX ${GLOG_SOURCES_DIR}
UPDATE_COMMAND ""
CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
-DCMAKE_INSTALL_PREFIX=${GLOG_INSTALL_DIR}
-DCMAKE_INSTALL_LIBDIR=${GLOG_INSTALL_DIR}/lib
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DWITH_GFLAGS=ON
-Dgflags_DIR=${GFLAGS_INSTALL_DIR}/lib/cmake/gflags
-DBUILD_TESTING=OFF
-DCMAKE_BUILD_TYPE=${THIRD_PARTY_BUILD_TYPE}
${EXTERNAL_OPTIONAL_ARGS}
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${GLOG_INSTALL_DIR}
-DCMAKE_INSTALL_LIBDIR:PATH=${GLOG_INSTALL_DIR}/lib
-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=${THIRD_PARTY_BUILD_TYPE}
)
ADD_LIBRARY(glog STATIC IMPORTED GLOBAL)
SET_PROPERTY(TARGET glog PROPERTY IMPORTED_LOCATION ${GLOG_LIBRARIES})
ADD_DEPENDENCIES(glog extern_glog gflags)
LINK_LIBRARIES(glog gflags)
LIST(APPEND external_project_dependencies glog)
IF(WITH_C_API)
INSTALL(DIRECTORY ${GLOG_INCLUDE_DIR} DESTINATION third_party/glog)
IF(ANDROID)
INSTALL(FILES ${GLOG_LIBRARIES} DESTINATION third_party/glog/lib/${ANDROID_ABI})
ELSE()
INSTALL(FILES ${GLOG_LIBRARIES} DESTINATION third_party/glog/lib)
ENDIF()
ENDIF()
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# 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.
IF(WITH_TESTING)
ENABLE_TESTING()
INCLUDE(ExternalProject)
SET(GTEST_SOURCES_DIR ${THIRD_PARTY_PATH}/gtest)
SET(GTEST_INSTALL_DIR ${THIRD_PARTY_PATH}/install/gtest)
SET(GTEST_INCLUDE_DIR "${GTEST_INSTALL_DIR}/include" CACHE PATH "gtest include directory." FORCE)
INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIR})
IF(WIN32)
set(GTEST_LIBRARIES
"${GTEST_INSTALL_DIR}/lib/gtest.lib" CACHE FILEPATH "gtest libraries." FORCE)
set(GTEST_MAIN_LIBRARIES
"${GTEST_INSTALL_DIR}/lib/gtest_main.lib" CACHE FILEPATH "gtest main libraries." FORCE)
ELSE(WIN32)
set(GTEST_LIBRARIES
"${GTEST_INSTALL_DIR}/lib/libgtest.a" CACHE FILEPATH "gtest libraries." FORCE)
set(GTEST_MAIN_LIBRARIES
"${GTEST_INSTALL_DIR}/lib/libgtest_main.a" CACHE FILEPATH "gtest main libraries." FORCE)
ENDIF(WIN32)
IF(WITH_MKLML)
# wait for mklml downloading completed
SET(GTEST_DEPENDS ${MKLML_PROJECT})
ENDIF()
ExternalProject_Add(
extern_gtest
${EXTERNAL_PROJECT_LOG_ARGS}
DEPENDS ${GTEST_DEPENDS}
GIT_REPOSITORY "https://github.com/google/googletest.git"
GIT_TAG "release-1.8.0"
PREFIX ${GTEST_SOURCES_DIR}
UPDATE_COMMAND ""
CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
-DCMAKE_INSTALL_PREFIX=${GTEST_INSTALL_DIR}
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DBUILD_GMOCK=ON
-Dgtest_disable_pthreads=ON
-Dgtest_force_shared_crt=ON
-DCMAKE_BUILD_TYPE=${THIRD_PARTY_BUILD_TYPE}
${EXTERNAL_OPTIONAL_ARGS}
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${GTEST_INSTALL_DIR}
-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=${THIRD_PARTY_BUILD_TYPE}
)
ADD_LIBRARY(gtest STATIC IMPORTED GLOBAL)
SET_PROPERTY(TARGET gtest PROPERTY IMPORTED_LOCATION ${GTEST_LIBRARIES})
ADD_DEPENDENCIES(gtest extern_gtest)
ADD_LIBRARY(gtest_main STATIC IMPORTED GLOBAL)
SET_PROPERTY(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${GTEST_MAIN_LIBRARIES})
ADD_DEPENDENCIES(gtest_main extern_gtest)
LIST(APPEND external_project_dependencies gtest gtest_main)
ENDIF(WITH_TESTING)
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# 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(ExternalProject)
# Always invoke `FIND_PACKAGE(Protobuf)` for importing function protobuf_generate_cpp
FIND_PACKAGE(Protobuf QUIET)
SET(PROTOBUF_FOUND "OFF")
if(NOT COMMAND protobuf_generate_python) # before cmake 3.4, protobuf_genrerate_python is not defined.
function(protobuf_generate_python SRCS)
# shameless copy from https://github.com/Kitware/CMake/blob/master/Modules/FindProtobuf.cmake
if(NOT ARGN)
message(SEND_ERROR "Error: PROTOBUF_GENERATE_PYTHON() called without any proto files")
return()
endif()
if(PROTOBUF_GENERATE_CPP_APPEND_PATH)
# Create an include path for each file specified
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(ABS_PATH ${ABS_FIL} PATH)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
else()
set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(DEFINED PROTOBUF_IMPORT_DIRS AND NOT DEFINED Protobuf_IMPORT_DIRS)
set(Protobuf_IMPORT_DIRS "${PROTOBUF_IMPORT_DIRS}")
endif()
if(DEFINED Protobuf_IMPORT_DIRS)
foreach(DIR ${Protobuf_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
endif()
set(${SRCS})
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
if(NOT PROTOBUF_GENERATE_CPP_APPEND_PATH)
get_filename_component(FIL_DIR ${FIL} DIRECTORY)
if(FIL_DIR)
set(FIL_WE "${FIL_DIR}/${FIL_WE}")
endif()
endif()
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}_pb2.py")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}_pb2.py"
COMMAND ${Protobuf_PROTOC_EXECUTABLE} --python_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL}
DEPENDS ${ABS_FIL} ${Protobuf_PROTOC_EXECUTABLE}
COMMENT "Running Python protocol buffer compiler on ${FIL}"
VERBATIM )
endforeach()
set(${SRCS} ${${SRCS}} PARENT_SCOPE)
endfunction()
endif()
# Print and set the protobuf library information,
# finish this cmake process and exit from this file.
macro(PROMPT_PROTOBUF_LIB)
SET(protobuf_DEPS ${ARGN})
MESSAGE(STATUS "Protobuf protoc executable: ${PROTOBUF_PROTOC_EXECUTABLE}")
MESSAGE(STATUS "Protobuf library: ${PROTOBUF_LIBRARY}")
MESSAGE(STATUS "Protobuf version: ${PROTOBUF_VERSION}")
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR})
# Assuming that all the protobuf libraries are of the same type.
IF(${PROTOBUF_LIBRARY} MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$")
SET(protobuf_LIBTYPE STATIC)
ELSEIF(${PROTOBUF_LIBRARY} MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$")
SET(protobuf_LIBTYPE SHARED)
ELSE()
MESSAGE(FATAL_ERROR "Unknown library type: ${PROTOBUF_LIBRARY}")
ENDIF()
ADD_LIBRARY(protobuf ${protobuf_LIBTYPE} IMPORTED GLOBAL)
SET_PROPERTY(TARGET protobuf PROPERTY IMPORTED_LOCATION ${PROTOBUF_LIBRARY})
ADD_LIBRARY(protobuf_lite ${protobuf_LIBTYPE} IMPORTED GLOBAL)
SET_PROPERTY(TARGET protobuf_lite PROPERTY IMPORTED_LOCATION ${PROTOBUF_LITE_LIBRARY})
ADD_LIBRARY(libprotoc ${protobuf_LIBTYPE} IMPORTED GLOBAL)
SET_PROPERTY(TARGET libprotoc PROPERTY IMPORTED_LOCATION ${PROTOC_LIBRARY})
ADD_EXECUTABLE(protoc IMPORTED GLOBAL)
SET_PROPERTY(TARGET protoc PROPERTY IMPORTED_LOCATION ${PROTOBUF_PROTOC_EXECUTABLE})
# FIND_Protobuf.cmake uses `Protobuf_PROTOC_EXECUTABLE`.
# make `protobuf_generate_cpp` happy.
SET(Protobuf_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE})
FOREACH(dep ${protobuf_DEPS})
ADD_DEPENDENCIES(protobuf ${dep})
ADD_DEPENDENCIES(protobuf_lite ${dep})
ADD_DEPENDENCIES(libprotoc ${dep})
ADD_DEPENDENCIES(protoc ${dep})
ENDFOREACH()
LIST(APPEND external_project_dependencies protobuf)
RETURN()
endmacro()
macro(SET_PROTOBUF_VERSION)
EXEC_PROGRAM(${PROTOBUF_PROTOC_EXECUTABLE} ARGS --version OUTPUT_VARIABLE PROTOBUF_VERSION)
STRING(REGEX MATCH "[0-9]+.[0-9]+" PROTOBUF_VERSION "${PROTOBUF_VERSION}")
endmacro()
set(PROTOBUF_ROOT "" CACHE PATH "Folder contains protobuf")
if (NOT "${PROTOBUF_ROOT}" STREQUAL "")
find_path(PROTOBUF_INCLUDE_DIR google/protobuf/message.h PATHS ${PROTOBUF_ROOT}/include)
find_library(PROTOBUF_LIBRARY protobuf PATHS ${PROTOBUF_ROOT}/lib)
find_library(PROTOBUF_LITE_LIBRARY protobuf-lite PATHS ${PROTOBUF_ROOT}/lib)
find_library(PROTOBUF_PROTOC_LIBRARY protoc PATHS ${PROTOBUF_ROOT}/lib)
find_program(PROTOBUF_PROTOC_EXECUTABLE protoc PATHS ${PROTOBUF_ROOT}/bin)
if (PROTOBUF_INCLUDE_DIR AND PROTOBUF_LIBRARY AND PROTOBUF_LITE_LIBRARY AND PROTOBUF_PROTOC_LIBRARY AND PROTOBUF_PROTOC_EXECUTABLE)
message(STATUS "Using custom protobuf library in ${PROTOBUF_ROOT}.")
SET_PROTOBUF_VERSION()
PROMPT_PROTOBUF_LIB()
else()
message(WARNING "Cannot find protobuf library in ${PROTOBUF_ROOT}.")
endif()
endif()
FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST)
STRING(REPLACE "extern_" "" TARGET_DIR_NAME "${TARGET_NAME}")
SET(PROTOBUF_SOURCES_DIR ${THIRD_PARTY_PATH}/${TARGET_DIR_NAME})
SET(PROTOBUF_INSTALL_DIR ${THIRD_PARTY_PATH}/install/${TARGET_DIR_NAME})
SET(${TARGET_NAME}_INCLUDE_DIR "${PROTOBUF_INSTALL_DIR}/include" PARENT_SCOPE)
SET(PROTOBUF_INCLUDE_DIR "${PROTOBUF_INSTALL_DIR}/include" PARENT_SCOPE)
SET(${TARGET_NAME}_LITE_LIBRARY
"${PROTOBUF_INSTALL_DIR}/lib/libprotobuf-lite${CMAKE_STATIC_LIBRARY_SUFFIX}"
PARENT_SCOPE)
SET(${TARGET_NAME}_LIBRARY
"${PROTOBUF_INSTALL_DIR}/lib/libprotobuf${CMAKE_STATIC_LIBRARY_SUFFIX}"
PARENT_SCOPE)
SET(${TARGET_NAME}_PROTOC_LIBRARY
"${PROTOBUF_INSTALL_DIR}/lib/libprotoc${CMAKE_STATIC_LIBRARY_SUFFIX}"
PARENT_SCOPE)
SET(${TARGET_NAME}_PROTOC_EXECUTABLE
"${PROTOBUF_INSTALL_DIR}/bin/protoc${CMAKE_EXECUTABLE_SUFFIX}"
PARENT_SCOPE)
SET(OPTIONAL_CACHE_ARGS "")
SET(OPTIONAL_ARGS "")
IF(BUILD_FOR_HOST)
SET(OPTIONAL_ARGS "-Dprotobuf_WITH_ZLIB=OFF")
ELSE()
SET(OPTIONAL_ARGS
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}"
"-Dprotobuf_WITH_ZLIB=ON"
"-DZLIB_ROOT:FILEPATH=${ZLIB_ROOT}"
${EXTERNAL_OPTIONAL_ARGS})
SET(OPTIONAL_CACHE_ARGS "-DZLIB_ROOT:STRING=${ZLIB_ROOT}")
ENDIF()
ExternalProject_Add(
${TARGET_NAME}
${EXTERNAL_PROJECT_LOG_ARGS}
PREFIX ${PROTOBUF_SOURCES_DIR}
UPDATE_COMMAND ""
DEPENDS zlib
GIT_REPOSITORY "https://github.com/google/protobuf.git"
GIT_TAG "9f75c5aa851cd877fb0d93ccc31b8567a6706546"
CONFIGURE_COMMAND
${CMAKE_COMMAND} ${PROTOBUF_SOURCES_DIR}/src/${TARGET_NAME}/cmake
${OPTIONAL_ARGS}
-Dprotobuf_BUILD_TESTS=OFF
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DCMAKE_BUILD_TYPE=${THIRD_PARTY_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${PROTOBUF_INSTALL_DIR}
-DCMAKE_INSTALL_LIBDIR=lib
CMAKE_CACHE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${PROTOBUF_INSTALL_DIR}
-DCMAKE_BUILD_TYPE:STRING=${THIRD_PARTY_BUILD_TYPE}
-DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF
-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON
${OPTIONAL_CACHE_ARGS}
)
ENDFUNCTION()
SET(PROTOBUF_VERSION 3.1)
IF(CMAKE_CROSSCOMPILING)
build_protobuf(protobuf_host TRUE)
LIST(APPEND external_project_dependencies protobuf_host)
SET(PROTOBUF_PROTOC_EXECUTABLE ${protobuf_host_PROTOC_EXECUTABLE}
CACHE FILEPATH "protobuf executable." FORCE)
ENDIF()
IF(NOT PROTOBUF_FOUND)
build_protobuf(extern_protobuf FALSE)
SET(PROTOBUF_INCLUDE_DIR ${extern_protobuf_INCLUDE_DIR}
CACHE PATH "protobuf include directory." FORCE)
SET(PROTOBUF_LITE_LIBRARY ${extern_protobuf_LITE_LIBRARY}
CACHE FILEPATH "protobuf lite library." FORCE)
SET(PROTOBUF_LIBRARY ${extern_protobuf_LIBRARY}
CACHE FILEPATH "protobuf library." FORCE)
SET(PROTOBUF_PROTOC_LIBRARY ${extern_protobuf_PROTOC_LIBRARY}
CACHE FILEPATH "protoc library." FORCE)
IF(WITH_C_API)
INSTALL(DIRECTORY ${PROTOBUF_INCLUDE_DIR} DESTINATION third_party/protobuf)
IF(ANDROID)
INSTALL(FILES ${PROTOBUF_LIBRARY} DESTINATION third_party/protobuf/lib/${ANDROID_ABI})
ELSE()
INSTALL(FILES ${PROTOBUF_LIBRARY} DESTINATION third_party/protobuf/lib)
ENDIF()
ENDIF()
IF(CMAKE_CROSSCOMPILING)
PROMPT_PROTOBUF_LIB(protobuf_host extern_protobuf)
ELSE()
SET(PROTOBUF_PROTOC_EXECUTABLE ${extern_protobuf_PROTOC_EXECUTABLE}
CACHE FILEPATH "protobuf executable." FORCE)
PROMPT_PROTOBUF_LIB(extern_protobuf)
ENDIF()
ENDIF(NOT PROTOBUF_FOUND)
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# 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(ExternalProject)
set(PYBIND_SOURCE_DIR ${THIRD_PARTY_PATH}/pybind)
include_directories(${PYBIND_SOURCE_DIR}/src/extern_pybind/include)
ExternalProject_Add(
extern_pybind
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/pybind/pybind11.git"
GIT_TAG "v2.1.1"
PREFIX ${PYBIND_SOURCE_DIR}
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
if(${CMAKE_VERSION} VERSION_LESS "3.3.0")
set(dummyfile ${CMAKE_CURRENT_BINARY_DIR}/pybind_dummy.c)
file(WRITE ${dummyfile} "const char * dummy_pybind = \"${dummyfile}\";")
add_library(pybind STATIC ${dummyfile})
else()
add_library(pybind INTERFACE)
endif()
add_dependencies(pybind extern_pybind)
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# 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(python_module)
FIND_PACKAGE(PythonInterp 2.7)
FIND_PACKAGE(PythonLibs 2.7)
# Fixme: Maybe find a static library. Get SHARED/STATIC by FIND_PACKAGE.
ADD_LIBRARY(python SHARED IMPORTED GLOBAL)
SET_PROPERTY(TARGET python PROPERTY IMPORTED_LOCATION ${PYTHON_LIBRARIES})
SET(py_env "")
IF(PYTHONINTERP_FOUND)
find_python_module(pip REQUIRED)
find_python_module(numpy REQUIRED)
find_python_module(wheel REQUIRED)
ENDIF(PYTHONINTERP_FOUND)
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${PYTHON_NUMPY_INCLUDE_DIR})
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# 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(ExternalProject)
SET(ZLIB_SOURCES_DIR ${THIRD_PARTY_PATH}/zlib)
SET(ZLIB_INSTALL_DIR ${THIRD_PARTY_PATH}/install/zlib)
SET(ZLIB_ROOT ${ZLIB_INSTALL_DIR} CACHE FILEPATH "zlib root directory." FORCE)
SET(ZLIB_INCLUDE_DIR "${ZLIB_INSTALL_DIR}/include" CACHE PATH "zlib include directory." FORCE)
IF(WIN32)
SET(ZLIB_LIBRARIES "${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib" CACHE FILEPATH "zlib library." FORCE)
ELSE(WIN32)
SET(ZLIB_LIBRARIES "${ZLIB_INSTALL_DIR}/lib/libz.a" CACHE FILEPATH "zlib library." FORCE)
ENDIF(WIN32)
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
ExternalProject_Add(
zlib
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/madler/zlib.git"
GIT_TAG "v1.2.8"
PREFIX ${ZLIB_SOURCES_DIR}
UPDATE_COMMAND ""
CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_INSTALL_PREFIX=${ZLIB_INSTALL_DIR}
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DCMAKE_MACOSX_RPATH=ON
-DCMAKE_BUILD_TYPE=${THIRD_PARTY_BUILD_TYPE}
${EXTERNAL_OPTIONAL_ARGS}
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${ZLIB_INSTALL_DIR}
-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=${THIRD_PARTY_BUILD_TYPE}
)
LIST(APPEND external_project_dependencies zlib)
IF(WITH_C_API)
INSTALL(DIRECTORY ${ZLIB_INCLUDE_DIR} DESTINATION third_party/zlib)
IF(ANDROID)
INSTALL(FILES ${ZLIB_LIBRARIES} DESTINATION third_party/zlib/lib/${ANDROID_ABI})
ELSE()
INSTALL(FILES ${ZLIB_LIBRARIES} DESTINATION third_party/zlib/lib)
ENDIF()
ENDIF()
# Find if a Python module is installed
# Found at http://www.cmake.org/pipermail/cmake/2011-January/041666.html
# To use do: find_python_module(PyQt4 REQUIRED)
function(find_python_module module)
string(TOUPPER ${module} module_upper)
if(NOT PY_${module_upper})
if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED")
set(${module}_FIND_REQUIRED TRUE)
else()
set(${module}_FIND_REQUIRED FALSE)
endif()
# A module's location is usually a directory, but for binary modules
# it's a .so file.
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))"
RESULT_VARIABLE _${module}_status
OUTPUT_VARIABLE _${module}_location
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _${module}_status)
set(PY_${module_upper} ${_${module}_location} CACHE STRING
"Location of Python module ${module}")
endif(NOT _${module}_status)
endif(NOT PY_${module_upper})
find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper})
if(NOT PY_${module_upper}_FOUND AND ${module}_FIND_REQUIRED)
message(FATAL_ERROR "python module ${module} is not found")
endif()
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import sys, ${module}; sys.stdout.write(${module}.__version__)"
OUTPUT_VARIABLE _${module}_version
RESULT_VARIABLE _${module}_status
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _${module}_status)
set(PY_${module_upper}_VERSION ${_${module}_version} CACHE STRING
"Version of Python module ${module}")
endif(NOT _${module}_status)
set(PY_${module_upper}_FOUND ${PY_${module_upper}_FOUND} PARENT_SCOPE)
set(PY_${module_upper}_VERSION ${PY_${module_upper}_VERSION} PARENT_SCOPE)
endfunction(find_python_module)
# 初步计划
支持类似TB中summary ops等可视化
- summary ops
- scalar
- histogram
- graph
- image
- audio
- text
# 参考文献
1. [tensorboard survey](https://github.com/VisualDL/VisualDL/wiki/tensorboard_survey)
# 后端架构设计
总体上划分为3层,由上至下分别为:
- Service Layer
- `Server` 提供服务
- `Frontend API` 后端前端的交互接口
- Logic Layer
- SDK, 负责为其他应用提供使用接口
- python SDK,方便基于 python 的应用的读取
- C SDK, 方便应用嵌入
- Information Maintainer, 负责周期性将数据预读到内存中,方便Server直接读取
- Storage Layer
- 负责Log 的结构化存储
- Protobuf API, 底层的存储结构
具体架构图如下
<p align="center">
<img src="./images/visualDL-backend-architecture.png"/>
</p>
以下展开一些基本的想法
## Service Layer
### server
- 基于简便的server框架实现,提供两种服务
- 作为main程序,支持一个前端网页
- 提供一系列的HTTP接口,以JSON格式支持与前端的交互
### Frontend API
- `Frontend API` 以单独模块的方式封装了与前端交互的接口逻辑,server依赖 `Frontend API` 与前端交互
## Logic Layer
### Information Maintainer (IM)
IM提供具体的数据处理以及计算,比如数据抽样,方差计算(histogram)等,
为了支持类似 embedding 等比较计算复杂的可视化,中间的计算结果以异步的方式更新。
IM 与 Server 的关系是
- IM 异步更新共享内存,Server 直接读取共享内存
- IM需要提供接口方便Server读取数据
### SDK
- Python SDK 用来支持任何Python界面的应用。
- 比如 Paddle, Tensorflow 等平台能够很方便地使用 Python SDK 初步支持可视化
- C SDK 用来方便嵌入到 C/C++ 的应用中
- 类似Paddle的平台可以通过嵌入 C 的方式实现原生可视化
## Storage Layer
### Protobuf API
- 决定了底层的数据格式,protobuf 提供了序列化的公共界面
- SDK通过Protobuf API操作底层数据
# 数据格式抽象
初版需要支持如下几种基础组件:
- 基本组件
- scalar
- histogram
- image
- audio
- text
- 高级组件
- graph
下面将对这些组件的数据类型进行抽象。
# 宏观信息
后端的日志会在每个周期追加到 `logdir` 中相应的log文件中,在追加信息的过程中,
除了graph,其他所有组件均需要时间步等信息,依次下面列出这个过程必须的一些元信息:
- `step_id` 追加的时间步
- `UNIX time` 方便追踪速度
- `tag` 类似TF,每个参与可视化的信息来源必须有一个唯一的命名(可以系统自动生成)
- tag会作为特定数据流从前端到后端统一的标识
# 各组件数据抽象
这里会抽象各组件数据必须存储的信息,为了便于沟通,使用了类似 `JSON` 的格式,
实际使用中,前后端可以自由选择数据格式,以及数据压缩。
## 抽样
每个 `tag` 会对应一个log文件,其中会包含全量信息(可以优化),但前端可视化并不需要全量信息,
这里后端就可以用 `蓄水池抽样` 来对全量数据进行抽样,组织好数据push给前端。
## 增量更新数据
考虑到前端可视化的动态更新,当前后端的全量数据应该只在第一次访问时才需要push给前端,
中间状态,后端前端交互的都只需要传递增量数据,具体增量数据格式类似
```javascript
[
[
"step_id": 200,
"data": <data_type>
]
]
```
## 统一的数据表示
上述除了group之外的所有组件的数据,都可以用下述数据格式表示
```
[
// data of each time-step
[
[
"step_id": 12,
"unix_time": 1234,
"data": [0.1, 0.2, ...]
],
// ... other steps
]
// meta info
[
// one of image, text, audio, scalar or histogram
"type": "image",
"size": [320, 270],
"tag": "some-name"
]
]
```
下面具体到单个组件,详细验证上述结构适用于每个组件
## scalar
```javascript
[
// step_id is hidden in this array's offsets
[
"unix_time" : 23323,
"data": 23
],
[
"unix_time" : 23325,
"data": 13
]
]
```
当然,上面的数据格式在最终存储时,可以折叠为
```
[
[23323, 23],
[23325, 13]
]
```
这里只关注必要的信息,具体的优化暂不考虑,后续不再赘述。
## histogram
histogram 需要得到tensor的分布,因此会把完整的tensor 的数值存下,具体的分布可以后端实时(增量)计算
后端存储的数据格式
```javascript
[
[
"step_id": 12,
"data": [0.12, 0.37, ...]
],
[
"step_id": 13,
"data": [0.13, 0.38, ...]
]
]
```
后端会计算好每个时间步的数据分布push给前端,具体数据格式如下
```javascript
[
[
"step_id": 12,
// n-dim distribution
"data": [0.12, 0.23, ...]
]
]
```
## image
考虑到图像为 `RGB` 格式,会对应至多3个矩阵,每个图片存储的数据格式如下
```javascript
[
"step_id": 12,
"type": "RGB",
"size": [345, 120],
"data": [0.1, 0.2, ...]
]
```
## audio
采样会是出的音频数据会是一个数组,因此类似如下格式
```javascript
[
"step_id": 12,
"type": "audio",
"size": [234],
"data": [...]
]
```
## text
后端直接生成 text便可
```javascript
[
"step_id": 12,
"data": "xx" // some text here
]
```
## graph
graph 会复杂一些,可能无法借用上述类型
```javascript
[
"nodes": [
[
[
"name": "node1",
"group": "scope1"
],
[
"name": "node2",
"group": "scope2"
]
]
],
// one edge
[
"from" : 0,
"to": 1,
"type": "normal"
],
[
"from" : 0,
"to": 1,
"type": "data"
]
]
```
# 可视化哪些信息(参数)
# 可视化形式
# 交互设计
# 前端增量
# 前端采样
{
"presets": [
["es2015", {"modules": false}],
"stage-0"
],
"plugins": ["transform-class-properties"]
}
node_modules/**/*
dep/**/*
test/**/*
mock/**/*
example/**/*
output/**/*
{
"files": [
"./src/**/*.san",
"./src/**/*.js",
"./template/**/*.html"
],
"eslint": {
"rules": {
"fecs-esnext-ext": [
"2",
[
"js",
"san"
]
],
"fecs-valid-jsdoc": [
"0"
]
}
},
"csshint": {},
"htmlcs": {},
"jformatter": {},
"esformatter": {},
"csscomb": {}
}
/node_modules
/.vscode
package-lock.json
# API
## runs
current runs
url: /data/runs
response:
```
["train", "test"]
```
## logdir
url: /data/logdir
response:
```
{
"logdir": "./tmp/tensorflow/mnist/logs/mnist_with_summaries/"
}
```
## data/plugins_listing
url: data/plugins_listing
\ No newline at end of file
/**
* get mock data
*
* @param {string} path request path
* @param {Object} queryParam query params
* @param {Object} postParam post params
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
if (queryParam.run === 'train') {
return {
// moock delay
_timeout: 0,
// mock http status
_status: 200,
// mock response data
_data: {
status: 0,
msg: 'SUCCESS',
data: [[1511842145.705075, 1, 0.05000000074505806], [1511842145.7388, 2, 0.12999999523162842], [1511842145.774563, 3, 0.27000001072883606], [1511842145.806828, 4, 0.4399999976158142], [1511842145.838082, 5, 0.47999998927116394], [1511842145.868955, 6, 0.5899999737739563], [1511842145.899323, 7, 0.6100000143051147], [1511842145.930518, 8, 0.699999988079071], [1511842145.96089, 9, 0.6700000166893005], [1511842146.460557, 11, 0.6499999761581421], [1511842146.4952, 12, 0.7300000190734863], [1511842146.525936, 13, 0.6899999976158142], [1511842146.556059, 14, 0.75], [1511842146.648703, 15, 0.7099999785423279], [1511842146.683295, 16, 0.7900000214576721], [1511842146.719782, 17, 0.8299999833106995], [1511842146.752392, 18, 0.7900000214576721], [1511842146.786562, 19, 0.8299999833106995], [1511842147.296362, 21, 0.8199999928474426], [1511842147.329616, 22, 0.75], [1511842147.36413, 23, 0.800000011920929], [1511842147.394166, 24, 0.8700000047683716], [1511842147.426248, 25, 0.8500000238418579], [1511842147.455792, 26, 0.8799999952316284], [1511842147.486918, 27, 0.8600000143051147], [1511842147.516537, 28, 0.8399999737739563], [1511842147.545978, 29, 0.8999999761581421], [1511842148.059008, 31, 0.8299999833106995], [1511842148.093655, 32, 0.8500000238418579], [1511842148.126041, 33, 0.8799999952316284], [1511842148.156253, 34, 0.9100000262260437], [1511842148.189653, 35, 0.8700000047683716], [1511842148.221602, 36, 0.800000011920929], [1511842148.251149, 37, 0.7900000214576721], [1511842148.281332, 38, 0.8700000047683716], [1511842148.312354, 39, 0.8500000238418579], [1511842148.828949, 41, 0.8700000047683716], [1511842148.865255, 42, 0.8600000143051147], [1511842148.898558, 43, 0.8999999761581421], [1511842148.93859, 44, 0.8799999952316284], [1511842148.97468, 45, 0.8799999952316284], [1511842149.013585, 46, 0.8299999833106995], [1511842149.044709, 47, 0.8500000238418579], [1511842149.077821, 48, 0.8700000047683716], [1511842149.107665, 49, 0.8899999856948853], [1511842149.647978, 51, 0.8600000143051147], [1511842149.686268, 52, 0.8999999761581421], [1511842149.722652, 53, 0.8700000047683716], [1511842149.758858, 54, 0.8700000047683716], [1511842149.795508, 55, 0.8999999761581421], [1511842149.830682, 56, 0.8700000047683716], [1511842149.874498, 57, 0.8600000143051147], [1511842149.916357, 58, 0.8399999737739563], [1511842149.951223, 59, 0.8600000143051147], [1511842150.470112, 61, 0.8500000238418579], [1511842150.505811, 62, 0.9399999976158142], [1511842150.542528, 63, 0.8399999737739563], [1511842150.579082, 64, 0.8999999761581421], [1511842150.611267, 65, 0.8799999952316284], [1511842150.642279, 66, 0.8500000238418579], [1511842150.679586, 67, 0.9399999976158142], [1511842150.709423, 68, 0.8999999761581421], [1511842150.742542, 69, 0.9200000166893005], [1511842151.287115, 71, 0.8700000047683716], [1511842151.320888, 72, 0.9200000166893005], [1511842151.352388, 73, 0.8600000143051147], [1511842151.385154, 74, 0.8600000143051147], [1511842151.421342, 75, 0.8399999737739563], [1511842151.458697, 76, 0.8999999761581421], [1511842151.492929, 77, 0.8399999737739563], [1511842151.524682, 78, 0.9200000166893005], [1511842151.55707, 79, 0.8799999952316284], [1511842152.071344, 81, 0.8500000238418579], [1511842152.101479, 82, 0.949999988079071], [1511842152.132134, 83, 0.8999999761581421], [1511842152.165848, 84, 0.8799999952316284], [1511842152.195162, 85, 0.8899999856948853], [1511842152.224646, 86, 0.9399999976158142], [1511842152.255385, 87, 0.9300000071525574], [1511842152.28573, 88, 0.9100000262260437], [1511842152.315899, 89, 0.8999999761581421], [1511842152.834572, 91, 0.8899999856948853], [1511842152.872045, 92, 0.949999988079071], [1511842152.904879, 93, 0.9800000190734863], [1511842152.940016, 94, 0.8700000047683716], [1511842152.976859, 95, 0.8500000238418579], [1511842153.012571, 96, 0.8999999761581421], [1511842153.043776, 97, 0.8199999928474426], [1511842153.081662, 98, 0.8799999952316284], [1511842153.190446, 99, 0.8700000047683716], [1511842153.698591, 101, 0.9200000166893005], [1511842153.732005, 102, 0.9300000071525574], [1511842153.765796, 103, 0.8500000238418579], [1511842153.797352, 104, 0.9700000286102295], [1511842153.831314, 105, 0.9599999785423279], [1511842153.866427, 106, 0.9300000071525574], [1511842153.896475, 107, 0.8999999761581421], [1511842153.929657, 108, 0.9200000166893005], [1511842153.965421, 109, 0.949999988079071], [1511842154.485552, 111, 0.949999988079071], [1511842154.516489, 112, 0.9100000262260437], [1511842154.548223, 113, 0.8799999952316284], [1511842154.580801, 114, 0.9200000166893005], [1511842154.611307, 115, 0.9200000166893005], [1511842154.642873, 116, 0.9200000166893005], [1511842154.6751, 117, 0.9300000071525574], [1511842154.707684, 118, 0.8999999761581421], [1511842154.744283, 119, 0.9300000071525574], [1511842155.255668, 121, 0.9200000166893005], [1511842155.288454, 122, 0.8899999856948853], [1511842155.320779, 123, 0.9599999785423279], [1511842155.353925, 124, 0.9200000166893005], [1511842155.389438, 125, 0.8899999856948853], [1511842155.424823, 126, 0.9399999976158142], [1511842155.454526, 127, 0.9300000071525574], [1511842155.489204, 128, 0.9399999976158142], [1511842155.521019, 129, 0.9399999976158142], [1511842156.033606, 131, 0.8999999761581421], [1511842156.071066, 132, 0.9100000262260437], [1511842156.100447, 133, 0.8899999856948853], [1511842156.132395, 134, 0.9200000166893005], [1511842156.165163, 135, 0.8700000047683716], [1511842156.203128, 136, 0.8999999761581421], [1511842156.239275, 137, 0.8399999737739563], [1511842156.270908, 138, 0.9200000166893005], [1511842156.303224, 139, 0.9200000166893005], [1511842156.823475, 141, 0.9300000071525574], [1511842156.860178, 142, 0.8999999761581421], [1511842156.89245, 143, 0.8500000238418579], [1511842156.932155, 144, 0.9300000071525574], [1511842156.966936, 145, 0.8999999761581421], [1511842157.002507, 146, 0.9100000262260437], [1511842157.04046, 147, 0.949999988079071], [1511842157.075624, 148, 0.8799999952316284], [1511842157.113062, 149, 0.9100000262260437], [1511842157.610962, 151, 0.9200000166893005], [1511842157.644282, 152, 0.8899999856948853], [1511842157.674532, 153, 0.9399999976158142], [1511842157.704223, 154, 0.9700000286102295], [1511842157.734917, 155, 0.9100000262260437], [1511842157.766545, 156, 0.949999988079071], [1511842157.803228, 157, 0.9300000071525574], [1511842157.833582, 158, 0.9399999976158142], [1511842157.863983, 159, 0.9599999785423279], [1511842158.365138, 161, 0.9399999976158142], [1511842158.402051, 162, 0.8999999761581421], [1511842158.432824, 163, 0.9100000262260437], [1511842158.468544, 164, 0.9399999976158142], [1511842158.501693, 165, 0.9200000166893005], [1511842158.536712, 166, 0.9200000166893005], [1511842158.576052, 167, 0.9100000262260437], [1511842158.608077, 168, 0.9300000071525574], [1511842158.639638, 169, 0.9399999976158142], [1511842159.144704, 171, 0.949999988079071], [1511842159.175508, 172, 0.9200000166893005], [1511842159.206246, 173, 0.9399999976158142], [1511842159.236448, 174, 0.949999988079071], [1511842159.268592, 175, 0.8999999761581421], [1511842159.298385, 176, 0.8999999761581421], [1511842159.330691, 177, 0.9200000166893005], [1511842159.362888, 178, 0.949999988079071], [1511842159.394601, 179, 0.8899999856948853], [1511842159.917987, 181, 0.8500000238418579], [1511842159.949645, 182, 0.9800000190734863], [1511842159.980383, 183, 0.9399999976158142], [1511842160.011846, 184, 0.8999999761581421], [1511842160.043909, 185, 0.8999999761581421], [1511842160.082747, 186, 0.8999999761581421], [1511842160.116125, 187, 0.949999988079071], [1511842160.147119, 188, 0.9100000262260437], [1511842160.182338, 189, 0.9599999785423279], [1511842160.69661, 191, 0.9300000071525574], [1511842160.72888, 192, 0.8899999856948853], [1511842160.762084, 193, 0.9300000071525574], [1511842160.792814, 194, 0.9599999785423279], [1511842160.823444, 195, 0.9100000262260437], [1511842160.852744, 196, 0.9399999976158142], [1511842160.885255, 197, 0.9599999785423279], [1511842160.917985, 198, 0.8999999761581421], [1511842161.028077, 199, 0.949999988079071], [1511842161.548108, 201, 0.9300000071525574], [1511842161.580669, 202, 0.9100000262260437], [1511842161.613862, 203, 0.949999988079071], [1511842161.653309, 204, 0.8899999856948853], [1511842161.691085, 205, 0.9599999785423279], [1511842161.72533, 206, 0.9200000166893005], [1511842161.754774, 207, 0.9300000071525574], [1511842161.786702, 208, 0.9300000071525574], [1511842161.820508, 209, 0.8999999761581421], [1511842162.336378, 211, 0.949999988079071], [1511842162.373533, 212, 0.949999988079071], [1511842162.411207, 213, 0.8899999856948853], [1511842162.4438, 214, 0.9300000071525574], [1511842162.477444, 215, 0.949999988079071], [1511842162.509545, 216, 0.9399999976158142], [1511842162.543586, 217, 0.949999988079071], [1511842162.574829, 218, 0.9300000071525574], [1511842162.612825, 219, 0.9399999976158142], [1511842163.134331, 221, 0.9700000286102295], [1511842163.169464, 222, 0.9200000166893005], [1511842163.203311, 223, 0.9700000286102295], [1511842163.240668, 224, 0.9300000071525574], [1511842163.278642, 225, 0.9100000262260437], [1511842163.318872, 226, 0.9300000071525574], [1511842163.361079, 227, 0.9300000071525574], [1511842163.397876, 228, 0.9800000190734863], [1511842163.430826, 229, 0.9599999785423279], [1511842163.953991, 231, 0.9300000071525574], [1511842163.988582, 232, 0.9399999976158142], [1511842164.021841, 233, 0.949999988079071], [1511842164.052817, 234, 0.9300000071525574], [1511842164.090031, 235, 0.9399999976158142], [1511842164.121797, 236, 0.9100000262260437], [1511842164.158074, 237, 0.949999988079071], [1511842164.189932, 238, 0.9300000071525574], [1511842164.225797, 239, 0.9200000166893005], [1511842164.742692, 241, 0.949999988079071], [1511842164.773757, 242, 0.9800000190734863], [1511842164.810333, 243, 0.9300000071525574], [1511842164.840904, 244, 0.949999988079071], [1511842164.875269, 245, 0.949999988079071], [1511842164.907458, 246, 0.9700000286102295], [1511842164.940818, 247, 0.9200000166893005], [1511842165.032471, 248, 0.9399999976158142], [1511842165.06494, 249, 0.9399999976158142], [1511842165.711591, 251, 0.9200000166893005], [1511842165.742034, 252, 0.9399999976158142], [1511842165.776186, 253, 0.9100000262260437], [1511842165.80631, 254, 0.9300000071525574], [1511842165.841813, 255, 0.9700000286102295], [1511842165.88272, 256, 0.9100000262260437], [1511842165.914834, 257, 0.9399999976158142], [1511842165.944832, 258, 0.8999999761581421], [1511842165.977476, 259, 0.8999999761581421], [1511842166.49414, 261, 0.9300000071525574], [1511842166.530139, 262, 0.9300000071525574], [1511842166.582532, 263, 0.9200000166893005], [1511842166.624934, 264, 0.9399999976158142], [1511842166.65914, 265, 0.9200000166893005], [1511842166.708159, 266, 0.949999988079071], [1511842166.738785, 267, 0.8899999856948853], [1511842166.789074, 268, 0.9599999785423279], [1511842166.821725, 269, 0.949999988079071], [1511842167.351619, 271, 0.8899999856948853], [1511842167.383981, 272, 0.949999988079071], [1511842167.415674, 273, 0.9300000071525574], [1511842167.453889, 274, 0.9300000071525574], [1511842167.487546, 275, 0.9800000190734863], [1511842167.520855, 276, 0.8999999761581421], [1511842167.553859, 277, 0.949999988079071], [1511842167.591104, 278, 0.9800000190734863], [1511842167.627239, 279, 0.9100000262260437], [1511842168.151087, 281, 0.9599999785423279], [1511842168.185722, 282, 0.9300000071525574], [1511842168.225793, 283, 0.9399999976158142], [1511842168.265352, 284, 0.9200000166893005], [1511842168.297638, 285, 0.9599999785423279], [1511842168.331366, 286, 0.8899999856948853], [1511842168.367654, 287, 0.949999988079071], [1511842168.404366, 288, 0.9200000166893005], [1511842168.437306, 289, 0.9599999785423279], [1511842168.977181, 291, 0.9700000286102295], [1511842169.008927, 292, 0.8999999761581421], [1511842169.043079, 293, 0.8999999761581421], [1511842169.079313, 294, 0.9599999785423279], [1511842169.111267, 295, 0.8899999856948853], [1511842169.141979, 296, 0.9700000286102295], [1511842169.173568, 297, 0.9399999976158142], [1511842169.206784, 298, 0.9399999976158142], [1511842169.318883, 299, 0.9100000262260437], [1511842169.836233, 301, 0.949999988079071], [1511842169.869173, 302, 0.9300000071525574], [1511842169.899444, 303, 0.9599999785423279], [1511842169.93236, 304, 0.9399999976158142], [1511842169.964287, 305, 0.9200000166893005], [1511842169.999156, 306, 0.9399999976158142], [1511842170.04658, 307, 0.949999988079071], [1511842170.079765, 308, 0.9399999976158142], [1511842170.116286, 309, 0.9100000262260437], [1511842170.626257, 311, 0.9599999785423279], [1511842170.660356, 312, 0.9399999976158142], [1511842170.69418, 313, 0.9200000166893005], [1511842170.72758, 314, 0.8999999761581421], [1511842170.761281, 315, 0.9399999976158142], [1511842170.795563, 316, 0.9399999976158142], [1511842170.831212, 317, 0.949999988079071], [1511842170.865901, 318, 0.9200000166893005], [1511842170.900623, 319, 0.9100000262260437], [1511842171.442059, 321, 0.9200000166893005], [1511842171.474961, 322, 0.8999999761581421], [1511842171.506307, 323, 0.9399999976158142], [1511842171.539442, 324, 0.949999988079071], [1511842171.570183, 325, 0.9700000286102295], [1511842171.600969, 326, 0.9300000071525574], [1511842171.6316, 327, 0.949999988079071], [1511842171.665024, 328, 0.9399999976158142], [1511842171.697585, 329, 0.9399999976158142], [1511842172.226505, 331, 0.9300000071525574], [1511842172.261344, 332, 0.949999988079071], [1511842172.296245, 333, 0.9300000071525574], [1511842172.326279, 334, 0.9700000286102295], [1511842172.358168, 335, 0.9100000262260437], [1511842172.392341, 336, 0.9399999976158142], [1511842172.422466, 337, 0.9399999976158142], [1511842172.459868, 338, 0.9599999785423279], [1511842172.493555, 339, 0.949999988079071], [1511842173.033582, 341, 0.949999988079071], [1511842173.073034, 342, 0.9300000071525574], [1511842173.104858, 343, 0.9599999785423279], [1511842173.140056, 344, 0.8899999856948853], [1511842173.178958, 345, 0.9399999976158142], [1511842173.21675, 346, 0.9300000071525574], [1511842173.246909, 347, 0.9700000286102295], [1511842173.282099, 348, 0.9100000262260437], [1511842173.317242, 349, 0.9599999785423279], [1511842173.847353, 351, 0.9599999785423279], [1511842173.885366, 352, 0.9599999785423279], [1511842173.921206, 353, 0.8999999761581421], [1511842173.953769, 354, 0.9399999976158142], [1511842173.991128, 355, 0.9599999785423279], [1511842174.02312, 356, 0.8700000047683716], [1511842174.056031, 357, 0.9399999976158142], [1511842174.08923, 358, 0.9599999785423279], [1511842174.127158, 359, 0.9800000190734863], [1511842174.652944, 361, 0.9200000166893005], [1511842174.687431, 362, 0.9900000095367432], [1511842174.71948, 363, 0.9399999976158142], [1511842174.753943, 364, 0.9599999785423279], [1511842174.78847, 365, 0.9700000286102295], [1511842174.823361, 366, 0.8799999952316284], [1511842174.856826, 367, 0.9300000071525574], [1511842174.891638, 368, 0.8999999761581421], [1511842174.925622, 369, 0.9599999785423279], [1511842175.439573, 371, 0.9700000286102295], [1511842175.474021, 372, 0.9300000071525574], [1511842175.504075, 373, 0.9599999785423279], [1511842175.540268, 374, 0.9599999785423279], [1511842175.574921, 375, 0.9800000190734863], [1511842175.608212, 376, 0.949999988079071], [1511842175.64061, 377, 0.949999988079071], [1511842175.671253, 378, 0.9399999976158142], [1511842175.702623, 379, 0.949999988079071], [1511842176.214339, 381, 0.949999988079071], [1511842176.248076, 382, 0.8999999761581421], [1511842176.286246, 383, 0.949999988079071], [1511842176.319941, 384, 0.9300000071525574], [1511842176.357204, 385, 0.9100000262260437], [1511842176.388956, 386, 0.949999988079071], [1511842176.419332, 387, 0.9300000071525574], [1511842176.451727, 388, 0.9599999785423279], [1511842176.484456, 389, 0.9399999976158142], [1511842177.034638, 391, 0.9800000190734863], [1511842177.067667, 392, 0.9200000166893005], [1511842177.101138, 393, 0.9200000166893005], [1511842177.135038, 394, 0.9700000286102295], [1511842177.170068, 395, 0.9399999976158142], [1511842177.200651, 396, 0.949999988079071], [1511842177.23465, 397, 0.9599999785423279], [1511842177.269173, 398, 0.9700000286102295], [1511842177.379705, 399, 0.9300000071525574], [1511842177.930872, 401, 0.9700000286102295], [1511842177.964943, 402, 0.9200000166893005], [1511842177.999324, 403, 0.949999988079071], [1511842178.033477, 404, 0.9900000095367432], [1511842178.071781, 405, 0.949999988079071], [1511842178.105857, 406, 0.949999988079071], [1511842178.135511, 407, 0.9300000071525574], [1511842178.167634, 408, 0.9300000071525574], [1511842178.199537, 409, 0.9200000166893005], [1511842178.717725, 411, 0.9399999976158142], [1511842178.74796, 412, 0.9399999976158142], [1511842178.780912, 413, 0.949999988079071], [1511842178.811067, 414, 0.949999988079071], [1511842178.84161, 415, 0.9399999976158142], [1511842178.878333, 416, 0.949999988079071], [1511842178.914753, 417, 0.9100000262260437], [1511842178.947455, 418, 0.9800000190734863], [1511842178.979425, 419, 0.9700000286102295], [1511842179.503057, 421, 0.9200000166893005], [1511842179.540604, 422, 0.9300000071525574], [1511842179.573449, 423, 0.9300000071525574], [1511842179.608729, 424, 0.949999988079071], [1511842179.645117, 425, 0.949999988079071], [1511842179.678533, 426, 0.9599999785423279], [1511842179.725027, 427, 0.9100000262260437], [1511842179.758991, 428, 0.9700000286102295], [1511842179.78958, 429, 0.9300000071525574], [1511842180.311945, 431, 0.9700000286102295], [1511842180.342856, 432, 0.9800000190734863], [1511842180.372838, 433, 0.949999988079071], [1511842180.402146, 434, 0.9100000262260437], [1511842180.43488, 435, 0.9200000166893005], [1511842180.467817, 436, 0.8999999761581421], [1511842180.497947, 437, 0.9599999785423279], [1511842180.529357, 438, 0.9399999976158142], [1511842180.561293, 439, 0.949999988079071], [1511842181.077772, 441, 0.9399999976158142], [1511842181.108623, 442, 0.949999988079071], [1511842181.140136, 443, 0.9900000095367432], [1511842181.174364, 444, 0.9700000286102295], [1511842181.20535, 445, 0.9300000071525574], [1511842181.23487, 446, 0.9700000286102295], [1511842181.267768, 447, 0.9200000166893005], [1511842181.296881, 448, 0.9599999785423279], [1511842181.331577, 449, 0.9599999785423279], [1511842181.84731, 451, 0.9700000286102295], [1511842181.877643, 452, 0.949999988079071], [1511842181.90728, 453, 0.9200000166893005], [1511842181.937846, 454, 0.9599999785423279], [1511842181.971116, 455, 0.949999988079071], [1511842182.000763, 456, 0.949999988079071], [1511842182.037276, 457, 0.9399999976158142], [1511842182.06702, 458, 0.9399999976158142], [1511842182.096206, 459, 0.9300000071525574], [1511842182.611312, 461, 0.949999988079071], [1511842182.64513, 462, 0.9599999785423279], [1511842182.674909, 463, 0.9300000071525574], [1511842182.705437, 464, 0.9300000071525574], [1511842182.735784, 465, 1.0], [1511842182.767558, 466, 0.949999988079071], [1511842182.796914, 467, 0.949999988079071], [1511842182.826817, 468, 0.8999999761581421], [1511842182.857851, 469, 0.9800000190734863], [1511842183.352468, 471, 0.9800000190734863], [1511842183.384449, 472, 0.8799999952316284], [1511842183.420997, 473, 0.9300000071525574], [1511842183.452375, 474, 0.9399999976158142], [1511842183.483695, 475, 0.949999988079071], [1511842183.514538, 476, 0.949999988079071], [1511842183.547066, 477, 0.9599999785423279], [1511842183.577378, 478, 0.9399999976158142], [1511842183.613494, 479, 0.949999988079071], [1511842184.166345, 481, 0.9599999785423279], [1511842184.198856, 482, 0.9300000071525574], [1511842184.231571, 483, 0.9399999976158142], [1511842184.261741, 484, 0.949999988079071], [1511842184.291412, 485, 0.9599999785423279], [1511842184.321249, 486, 0.9900000095367432], [1511842184.351878, 487, 0.9300000071525574], [1511842184.381002, 488, 0.9399999976158142], [1511842184.410772, 489, 0.9700000286102295], [1511842184.908431, 491, 0.949999988079071], [1511842184.941166, 492, 0.949999988079071], [1511842184.978217, 493, 0.9700000286102295], [1511842185.008197, 494, 0.9399999976158142], [1511842185.039879, 495, 0.9800000190734863], [1511842185.070592, 496, 0.9599999785423279], [1511842185.10259, 497, 0.9599999785423279], [1511842185.133795, 498, 0.9900000095367432], [1511842185.243204, 499, 0.9599999785423279], [1511842185.856761, 501, 0.949999988079071], [1511842185.896018, 502, 0.9700000286102295], [1511842185.943021, 503, 0.949999988079071], [1511842185.981642, 504, 0.949999988079071], [1511842186.023147, 505, 0.9800000190734863], [1511842186.058391, 506, 0.949999988079071], [1511842186.092093, 507, 0.9599999785423279], [1511842186.134389, 508, 0.9200000166893005], [1511842186.168454, 509, 0.9300000071525574], [1511842186.73473, 511, 0.9399999976158142], [1511842186.765813, 512, 0.9399999976158142], [1511842186.798392, 513, 0.9599999785423279], [1511842186.832886, 514, 0.9700000286102295], [1511842186.869, 515, 0.9399999976158142], [1511842186.899931, 516, 0.9399999976158142], [1511842186.92933, 517, 0.9700000286102295], [1511842186.961637, 518, 0.949999988079071], [1511842186.993339, 519, 0.949999988079071], [1511842187.51295, 521, 0.949999988079071], [1511842187.542084, 522, 0.9300000071525574], [1511842187.571555, 523, 0.9800000190734863], [1511842187.600865, 524, 0.949999988079071], [1511842187.630905, 525, 0.9700000286102295], [1511842187.66434, 526, 0.9599999785423279], [1511842187.695883, 527, 0.9399999976158142], [1511842187.734118, 528, 0.949999988079071], [1511842187.768383, 529, 0.9399999976158142], [1511842188.2609, 531, 0.9599999785423279], [1511842188.291538, 532, 0.949999988079071], [1511842188.324743, 533, 0.949999988079071], [1511842188.359286, 534, 0.9399999976158142], [1511842188.392047, 535, 0.9300000071525574], [1511842188.421359, 536, 0.9399999976158142], [1511842188.454753, 537, 0.949999988079071], [1511842188.48794, 538, 0.9900000095367432], [1511842188.524741, 539, 0.9599999785423279], [1511842189.0112, 541, 0.949999988079071], [1511842189.043185, 542, 0.9300000071525574], [1511842189.074307, 543, 0.9700000286102295], [1511842189.106053, 544, 0.9399999976158142], [1511842189.135565, 545, 0.9700000286102295], [1511842189.168538, 546, 0.9599999785423279], [1511842189.201185, 547, 0.9399999976158142], [1511842189.235236, 548, 0.9900000095367432], [1511842189.26819, 549, 0.949999988079071], [1511842189.760432, 551, 0.9300000071525574], [1511842189.789696, 552, 0.9599999785423279], [1511842189.818744, 553, 0.9200000166893005], [1511842189.848042, 554, 0.949999988079071], [1511842189.877089, 555, 0.9399999976158142], [1511842189.907031, 556, 0.949999988079071], [1511842189.936881, 557, 0.9800000190734863], [1511842189.966071, 558, 0.9399999976158142], [1511842189.995653, 559, 0.9900000095367432], [1511842190.508212, 561, 0.9200000166893005], [1511842190.540299, 562, 0.9800000190734863], [1511842190.578574, 563, 0.9300000071525574], [1511842190.610377, 564, 0.949999988079071], [1511842190.643136, 565, 0.9399999976158142], [1511842190.672663, 566, 1.0], [1511842190.707029, 567, 0.9900000095367432], [1511842190.739414, 568, 0.949999988079071], [1511842190.77237, 569, 0.9599999785423279], [1511842191.31564, 571, 0.9300000071525574], [1511842191.348465, 572, 0.9900000095367432], [1511842191.382138, 573, 0.949999988079071], [1511842191.412707, 574, 0.9399999976158142], [1511842191.443585, 575, 0.9599999785423279], [1511842191.477383, 576, 0.9700000286102295], [1511842191.510109, 577, 0.9399999976158142], [1511842191.540084, 578, 0.9399999976158142], [1511842191.569611, 579, 0.9599999785423279], [1511842192.080061, 581, 0.949999988079071], [1511842192.114685, 582, 0.949999988079071], [1511842192.146026, 583, 0.9599999785423279], [1511842192.179577, 584, 0.9300000071525574], [1511842192.213491, 585, 0.949999988079071], [1511842192.247531, 586, 0.949999988079071], [1511842192.279046, 587, 0.9700000286102295], [1511842192.3092, 588, 0.9300000071525574], [1511842192.343594, 589, 0.9700000286102295], [1511842192.842748, 591, 0.9599999785423279], [1511842192.872712, 592, 0.9700000286102295], [1511842192.903806, 593, 0.9700000286102295], [1511842192.935047, 594, 0.949999988079071], [1511842192.966689, 595, 0.9300000071525574], [1511842192.996178, 596, 1.0], [1511842193.025323, 597, 0.9300000071525574], [1511842193.059378, 598, 0.949999988079071], [1511842193.161189, 599, 0.9599999785423279], [1511842193.658418, 601, 0.9900000095367432], [1511842193.688131, 602, 0.9399999976158142], [1511842193.720422, 603, 0.9800000190734863], [1511842193.752308, 604, 0.9599999785423279], [1511842193.782545, 605, 0.949999988079071], [1511842193.814567, 606, 0.9599999785423279], [1511842193.848188, 607, 0.9399999976158142], [1511842193.879656, 608, 0.9700000286102295], [1511842193.910992, 609, 0.9900000095367432], [1511842194.408775, 611, 0.949999988079071], [1511842194.557519, 612, 0.9900000095367432], [1511842194.59024, 613, 0.9700000286102295], [1511842194.62302, 614, 0.9399999976158142], [1511842194.656742, 615, 0.9599999785423279], [1511842194.68816, 616, 0.9900000095367432], [1511842194.720722, 617, 0.9300000071525574], [1511842194.752064, 618, 0.9800000190734863], [1511842194.784808, 619, 0.9100000262260437], [1511842195.290169, 621, 0.949999988079071], [1511842195.323044, 622, 0.9700000286102295], [1511842195.353092, 623, 0.9900000095367432], [1511842195.386405, 624, 0.949999988079071], [1511842195.417719, 625, 0.9700000286102295], [1511842195.471398, 626, 0.9800000190734863], [1511842195.508423, 627, 0.9599999785423279], [1511842195.543529, 628, 0.9700000286102295], [1511842195.582063, 629, 0.9700000286102295], [1511842196.143761, 631, 0.9700000286102295], [1511842196.182851, 632, 0.9700000286102295], [1511842196.214629, 633, 0.9700000286102295], [1511842196.253051, 634, 0.9700000286102295], [1511842196.287821, 635, 0.949999988079071], [1511842196.319766, 636, 0.9399999976158142], [1511842196.354184, 637, 0.9399999976158142], [1511842196.38808, 638, 0.9599999785423279], [1511842196.421604, 639, 0.949999988079071], [1511842196.947931, 641, 1.0], [1511842196.977982, 642, 0.949999988079071], [1511842197.013177, 643, 0.9599999785423279], [1511842197.053432, 644, 0.9599999785423279], [1511842197.089024, 645, 0.9700000286102295], [1511842197.120129, 646, 0.9599999785423279], [1511842197.150194, 647, 0.9300000071525574], [1511842197.18433, 648, 0.9700000286102295], [1511842197.214245, 649, 0.9599999785423279], [1511842197.734399, 651, 0.9599999785423279], [1511842197.765878, 652, 0.949999988079071], [1511842197.795536, 653, 0.9300000071525574], [1511842197.828955, 654, 0.949999988079071], [1511842197.858018, 655, 0.949999988079071], [1511842197.88918, 656, 0.9300000071525574], [1511842197.921247, 657, 0.9599999785423279], [1511842197.961132, 658, 0.9599999785423279], [1511842197.990723, 659, 0.949999988079071], [1511842198.489996, 661, 0.9700000286102295], [1511842198.520217, 662, 0.9900000095367432], [1511842198.550925, 663, 1.0], [1511842198.582604, 664, 0.9700000286102295], [1511842198.612304, 665, 0.9700000286102295], [1511842198.648211, 666, 0.9300000071525574], [1511842198.686078, 667, 0.9599999785423279], [1511842198.717921, 668, 0.949999988079071], [1511842198.750968, 669, 0.9599999785423279], [1511842199.258821, 671, 0.9399999976158142], [1511842199.291037, 672, 0.9800000190734863], [1511842199.320875, 673, 0.9700000286102295], [1511842199.352976, 674, 0.949999988079071], [1511842199.382681, 675, 0.9800000190734863], [1511842199.411833, 676, 0.949999988079071], [1511842199.443132, 677, 0.9700000286102295], [1511842199.475117, 678, 0.9700000286102295], [1511842199.505664, 679, 0.949999988079071], [1511842200.00445, 681, 0.949999988079071], [1511842200.037647, 682, 0.9900000095367432], [1511842200.073666, 683, 0.9200000166893005], [1511842200.104847, 684, 0.9200000166893005], [1511842200.137768, 685, 0.9300000071525574], [1511842200.170784, 686, 0.9800000190734863], [1511842200.204724, 687, 0.9800000190734863], [1511842200.240477, 688, 0.9800000190734863], [1511842200.275783, 689, 0.9900000095367432], [1511842200.81989, 691, 0.9700000286102295], [1511842200.858525, 692, 0.9300000071525574], [1511842200.890363, 693, 0.949999988079071], [1511842200.932829, 694, 0.9599999785423279], [1511842200.962743, 695, 0.9800000190734863], [1511842200.994843, 696, 0.9200000166893005], [1511842201.03947, 697, 0.9800000190734863], [1511842201.070836, 698, 0.9399999976158142], [1511842201.246026, 699, 0.9100000262260437], [1511842201.786404, 701, 0.9300000071525574], [1511842201.819202, 702, 0.9800000190734863], [1511842201.856957, 703, 0.9200000166893005], [1511842201.888767, 704, 1.0], [1511842201.92143, 705, 0.9399999976158142], [1511842201.961242, 706, 0.9700000286102295], [1511842201.996298, 707, 0.9200000166893005], [1511842202.032258, 708, 0.9800000190734863], [1511842202.069833, 709, 0.9300000071525574], [1511842202.5957, 711, 0.9800000190734863], [1511842202.632072, 712, 0.9800000190734863], [1511842202.664469, 713, 0.9700000286102295], [1511842202.696135, 714, 0.9300000071525574], [1511842202.72853, 715, 0.9700000286102295], [1511842202.759628, 716, 0.9800000190734863], [1511842202.794048, 717, 0.9800000190734863], [1511842202.827036, 718, 0.949999988079071], [1511842202.858473, 719, 0.9700000286102295], [1511842203.391985, 721, 0.9599999785423279], [1511842203.4215, 722, 0.9399999976158142], [1511842203.453421, 723, 0.9800000190734863], [1511842203.484233, 724, 0.9700000286102295], [1511842203.516743, 725, 0.9900000095367432], [1511842203.550632, 726, 0.9800000190734863], [1511842203.584461, 727, 0.9800000190734863], [1511842203.617915, 728, 0.9599999785423279], [1511842203.652398, 729, 0.9700000286102295], [1511842204.185468, 731, 0.9800000190734863], [1511842204.217265, 732, 0.9800000190734863], [1511842204.249281, 733, 1.0], [1511842204.282808, 734, 0.9700000286102295], [1511842204.314277, 735, 0.9700000286102295], [1511842204.345353, 736, 0.949999988079071], [1511842204.375898, 737, 0.9599999785423279], [1511842204.411013, 738, 0.9100000262260437], [1511842204.441207, 739, 0.9700000286102295], [1511842204.967109, 741, 0.9399999976158142], [1511842204.999614, 742, 0.9900000095367432], [1511842205.037706, 743, 0.9599999785423279], [1511842205.071398, 744, 0.9700000286102295], [1511842205.109827, 745, 0.9800000190734863], [1511842205.150377, 746, 0.9700000286102295], [1511842205.196859, 747, 0.9399999976158142], [1511842205.238587, 748, 0.9900000095367432], [1511842205.284592, 749, 0.9700000286102295], [1511842205.913203, 751, 0.9599999785423279], [1511842205.977628, 752, 0.9800000190734863], [1511842206.049908, 753, 0.9399999976158142], [1511842206.088929, 754, 0.9599999785423279], [1511842206.12656, 755, 0.9700000286102295], [1511842206.171609, 756, 0.9700000286102295], [1511842206.207161, 757, 0.9599999785423279], [1511842206.259537, 758, 0.9900000095367432], [1511842206.299124, 759, 0.9800000190734863], [1511842206.839902, 761, 0.9399999976158142], [1511842206.869605, 762, 0.9800000190734863], [1511842206.901015, 763, 0.9599999785423279], [1511842206.931029, 764, 0.9700000286102295], [1511842206.961387, 765, 0.9300000071525574], [1511842206.992158, 766, 0.9599999785423279], [1511842207.02291, 767, 0.9700000286102295], [1511842207.055302, 768, 0.9599999785423279], [1511842207.089098, 769, 0.9399999976158142], [1511842207.593272, 771, 0.9800000190734863], [1511842207.624582, 772, 0.949999988079071], [1511842207.654845, 773, 0.9599999785423279], [1511842207.688258, 774, 0.9800000190734863], [1511842207.718489, 775, 0.9700000286102295], [1511842207.747767, 776, 0.949999988079071], [1511842207.778593, 777, 0.9700000286102295], [1511842207.808232, 778, 0.9599999785423279], [1511842207.838551, 779, 0.9599999785423279], [1511842208.397092, 781, 0.9700000286102295], [1511842208.427281, 782, 0.949999988079071], [1511842208.458664, 783, 0.9800000190734863], [1511842208.488235, 784, 0.9599999785423279], [1511842208.519785, 785, 0.9599999785423279], [1511842208.554034, 786, 0.9800000190734863], [1511842208.586204, 787, 0.9800000190734863], [1511842208.619628, 788, 0.9700000286102295], [1511842208.654394, 789, 0.9800000190734863], [1511842209.194778, 791, 0.9800000190734863], [1511842209.226194, 792, 0.9599999785423279], [1511842209.266281, 793, 0.9599999785423279], [1511842209.295469, 794, 0.9800000190734863], [1511842209.327287, 795, 0.9800000190734863], [1511842209.364207, 796, 0.9599999785423279], [1511842209.395033, 797, 0.9599999785423279], [1511842209.429895, 798, 0.949999988079071], [1511842209.556844, 799, 0.949999988079071], [1511842210.130269, 801, 0.9700000286102295], [1511842210.160287, 802, 0.9700000286102295], [1511842210.193199, 803, 0.9700000286102295], [1511842210.22632, 804, 0.9399999976158142], [1511842210.26311, 805, 0.9800000190734863], [1511842210.309019, 806, 0.9599999785423279], [1511842210.347609, 807, 0.9800000190734863], [1511842210.390996, 808, 0.9900000095367432], [1511842210.439887, 809, 0.9200000166893005], [1511842211.003122, 811, 0.9599999785423279], [1511842211.037074, 812, 0.9599999785423279], [1511842211.067644, 813, 0.9399999976158142], [1511842211.101507, 814, 0.9599999785423279], [1511842211.140325, 815, 0.9599999785423279], [1511842211.174903, 816, 0.949999988079071], [1511842211.224003, 817, 0.9700000286102295], [1511842211.286801, 818, 0.9900000095367432], [1511842211.327776, 819, 0.9800000190734863], [1511842211.985095, 821, 0.9200000166893005], [1511842212.030882, 822, 0.9700000286102295], [1511842212.070569, 823, 0.9399999976158142], [1511842212.118181, 824, 0.9800000190734863], [1511842212.157094, 825, 0.9599999785423279], [1511842212.226286, 826, 0.9599999785423279], [1511842212.277023, 827, 0.9900000095367432], [1511842212.311592, 828, 1.0], [1511842212.348642, 829, 0.9599999785423279], [1511842212.91796, 831, 0.9900000095367432], [1511842212.950586, 832, 0.9800000190734863], [1511842212.996488, 833, 0.9599999785423279], [1511842213.027555, 834, 0.9800000190734863], [1511842213.058825, 835, 0.9800000190734863], [1511842213.091046, 836, 0.9599999785423279], [1511842213.125024, 837, 0.9599999785423279], [1511842213.160207, 838, 0.9399999976158142], [1511842213.191536, 839, 0.9700000286102295], [1511842213.728065, 841, 0.9700000286102295], [1511842213.762401, 842, 0.9700000286102295], [1511842213.799732, 843, 0.9700000286102295], [1511842213.833657, 844, 0.9800000190734863], [1511842213.866312, 845, 0.9800000190734863], [1511842213.902647, 846, 0.9599999785423279], [1511842213.942028, 847, 0.9700000286102295], [1511842213.983694, 848, 0.9599999785423279], [1511842214.017235, 849, 0.949999988079071], [1511842214.539192, 851, 0.9800000190734863], [1511842214.570802, 852, 0.9599999785423279], [1511842214.602371, 853, 0.9800000190734863], [1511842214.63469, 854, 0.9599999785423279], [1511842214.666527, 855, 0.9700000286102295], [1511842214.7013, 856, 0.949999988079071], [1511842214.736796, 857, 0.9700000286102295], [1511842214.773076, 858, 0.9900000095367432], [1511842214.804659, 859, 0.9900000095367432], [1511842215.333667, 861, 0.9700000286102295], [1511842215.36866, 862, 0.9800000190734863], [1511842215.408818, 863, 0.9800000190734863], [1511842215.446431, 864, 0.9900000095367432], [1511842215.484602, 865, 0.9399999976158142], [1511842215.520258, 866, 0.9800000190734863], [1511842215.555225, 867, 0.9800000190734863], [1511842215.592115, 868, 0.9599999785423279], [1511842215.630665, 869, 0.9700000286102295], [1511842216.128021, 871, 1.0], [1511842216.159525, 872, 0.9200000166893005], [1511842216.191044, 873, 0.9800000190734863], [1511842216.22625, 874, 0.949999988079071], [1511842216.264204, 875, 1.0], [1511842216.301708, 876, 0.9300000071525574], [1511842216.346822, 877, 0.949999988079071], [1511842216.387908, 878, 0.9900000095367432], [1511842216.431012, 879, 0.9399999976158142], [1511842216.948288, 881, 0.9800000190734863], [1511842216.980613, 882, 0.9599999785423279], [1511842217.01133, 883, 0.9700000286102295], [1511842217.042047, 884, 0.9900000095367432], [1511842217.073482, 885, 0.9300000071525574], [1511842217.108726, 886, 0.9599999785423279], [1511842217.14001, 887, 0.9800000190734863], [1511842217.171317, 888, 0.9599999785423279], [1511842217.203606, 889, 0.9800000190734863], [1511842217.753845, 891, 0.9399999976158142], [1511842217.789057, 892, 0.9599999785423279], [1511842217.830139, 893, 0.9800000190734863], [1511842217.864796, 894, 0.949999988079071], [1511842217.897203, 895, 0.9800000190734863], [1511842217.929849, 896, 0.949999988079071], [1511842217.963099, 897, 0.9800000190734863], [1511842217.996219, 898, 0.9599999785423279], [1511842218.111479, 899, 0.9599999785423279], [1511842218.683541, 901, 0.9700000286102295], [1511842218.714867, 902, 0.9900000095367432], [1511842218.745403, 903, 0.949999988079071], [1511842218.775027, 904, 0.9599999785423279], [1511842218.804858, 905, 0.9700000286102295], [1511842218.834642, 906, 0.9599999785423279], [1511842218.865326, 907, 0.9900000095367432], [1511842218.896291, 908, 0.9700000286102295], [1511842218.926799, 909, 0.9800000190734863], [1511842219.457638, 911, 0.949999988079071], [1511842219.489308, 912, 0.9700000286102295], [1511842219.524922, 913, 0.9100000262260437], [1511842219.55795, 914, 0.9900000095367432], [1511842219.595283, 915, 0.9900000095367432], [1511842219.687366, 916, 0.9900000095367432], [1511842219.727115, 917, 0.9800000190734863], [1511842219.768945, 918, 0.9599999785423279], [1511842219.801816, 919, 0.9800000190734863], [1511842220.390596, 921, 0.9900000095367432], [1511842220.43033, 922, 0.9700000286102295], [1511842220.469885, 923, 0.9700000286102295], [1511842220.501432, 924, 0.949999988079071], [1511842220.534336, 925, 0.949999988079071], [1511842220.577459, 926, 0.9800000190734863], [1511842220.634076, 927, 0.9700000286102295], [1511842220.668908, 928, 0.9599999785423279], [1511842220.7005, 929, 0.9900000095367432], [1511842221.287896, 931, 0.9800000190734863], [1511842221.319684, 932, 0.9599999785423279], [1511842221.355376, 933, 0.949999988079071], [1511842221.390386, 934, 0.9700000286102295], [1511842221.424849, 935, 0.9599999785423279], [1511842221.455576, 936, 0.9800000190734863], [1511842221.488522, 937, 0.9700000286102295], [1511842221.523331, 938, 0.949999988079071], [1511842221.562103, 939, 0.9800000190734863], [1511842222.11082, 941, 0.9399999976158142], [1511842222.144042, 942, 1.0], [1511842222.180893, 943, 0.9599999785423279], [1511842222.214718, 944, 0.9599999785423279], [1511842222.250779, 945, 0.9399999976158142], [1511842222.283768, 946, 0.9599999785423279], [1511842222.318964, 947, 0.949999988079071], [1511842222.351751, 948, 0.9599999785423279], [1511842222.382504, 949, 0.9800000190734863], [1511842222.889534, 951, 0.9800000190734863], [1511842222.921617, 952, 0.949999988079071], [1511842222.953747, 953, 0.9399999976158142], [1511842222.989928, 954, 0.9399999976158142], [1511842223.023551, 955, 0.9700000286102295], [1511842223.060438, 956, 0.9800000190734863], [1511842223.097254, 957, 0.9599999785423279], [1511842223.138771, 958, 0.9800000190734863], [1511842223.174054, 959, 0.9399999976158142], [1511842223.693137, 961, 0.949999988079071], [1511842223.723562, 962, 0.9800000190734863], [1511842223.755989, 963, 0.9700000286102295], [1511842223.790843, 964, 0.9900000095367432], [1511842223.822565, 965, 0.9800000190734863], [1511842223.853841, 966, 0.9900000095367432], [1511842223.892184, 967, 0.9399999976158142], [1511842223.928116, 968, 0.9800000190734863], [1511842223.961467, 969, 0.9700000286102295], [1511842224.536617, 971, 0.9900000095367432], [1511842224.577399, 972, 0.9599999785423279], [1511842224.617609, 973, 0.9700000286102295], [1511842224.655355, 974, 0.9599999785423279], [1511842224.689069, 975, 0.949999988079071], [1511842224.72824, 976, 0.9700000286102295], [1511842224.759455, 977, 0.949999988079071], [1511842224.793144, 978, 0.9599999785423279], [1511842224.824675, 979, 0.9700000286102295], [1511842225.342267, 981, 0.9599999785423279], [1511842225.374886, 982, 0.9700000286102295], [1511842225.407038, 983, 0.9800000190734863], [1511842225.438013, 984, 1.0], [1511842225.468219, 985, 0.9800000190734863], [1511842225.499246, 986, 0.9800000190734863], [1511842225.528987, 987, 0.949999988079071], [1511842225.56184, 988, 0.9599999785423279], [1511842225.596399, 989, 0.949999988079071], [1511842226.096082, 991, 0.9399999976158142], [1511842226.125964, 992, 0.9800000190734863], [1511842226.156644, 993, 0.9599999785423279], [1511842226.187556, 994, 0.9599999785423279], [1511842226.218394, 995, 0.9900000095367432], [1511842226.250693, 996, 0.9599999785423279], [1511842226.284415, 997, 0.9800000190734863], [1511842226.316085, 998, 0.9700000286102295], [1511842226.42392, 999, 0.9599999785423279]]
}
}
}
else {
return {
// moock delay
_timeout: 0,
// mock http status
_status: 200,
// mock response data
_data: {
status: 0,
msg: 'SUCCESS',
data: [[1511842145.514333, 0, 0.094200000166893], [1511842146.427384, 10, 0.7394000291824341], [1511842147.260405, 20, 0.8302000164985657], [1511842148.019018, 30, 0.8662999868392944], [1511842148.793569, 40, 0.8877999782562256], [1511842149.610228, 50, 0.8952000141143799], [1511842150.437095, 60, 0.9031000137329102], [1511842151.254679, 70, 0.9004999995231628], [1511842152.039353, 80, 0.9110000133514404], [1511842152.800043, 90, 0.9143000245094299], [1511842153.668115, 100, 0.9175000190734863], [1511842154.453025, 110, 0.9190000295639038], [1511842155.224028, 120, 0.9214000105857849], [1511842156.001674, 130, 0.9204999804496765], [1511842156.788682, 140, 0.9297999739646912], [1511842157.580495, 150, 0.9289000034332275], [1511842158.331122, 160, 0.9283999800682068], [1511842159.112936, 170, 0.9298999905586243], [1511842159.881351, 180, 0.9301000237464905], [1511842160.659058, 190, 0.9330999851226807], [1511842161.515267, 200, 0.9345999956130981], [1511842162.301136, 210, 0.9351000189781189], [1511842163.092769, 220, 0.9343000054359436], [1511842163.920516, 230, 0.9372000098228455], [1511842164.705352, 240, 0.9351999759674072], [1511842165.674882, 250, 0.9369999766349792], [1511842166.462909, 260, 0.9444000124931335], [1511842167.319383, 270, 0.9437000155448914], [1511842168.114944, 280, 0.9441999793052673], [1511842168.942822, 290, 0.9441999793052673], [1511842169.801691, 300, 0.9453999996185303], [1511842170.594651, 310, 0.9419000148773193], [1511842171.406169, 320, 0.9437000155448914], [1511842172.190729, 330, 0.9488999843597412], [1511842172.997593, 340, 0.9483000040054321], [1511842173.815758, 350, 0.9453999996185303], [1511842174.613314, 360, 0.9480000138282776], [1511842175.402401, 370, 0.9513999819755554], [1511842176.180888, 380, 0.9480999708175659], [1511842176.999133, 390, 0.9520000219345093], [1511842177.900425, 400, 0.954200029373169], [1511842178.682129, 410, 0.9490000009536743], [1511842179.471428, 420, 0.9534000158309937], [1511842180.272808, 430, 0.9549999833106995], [1511842181.044304, 440, 0.9545999765396118], [1511842181.814283, 450, 0.951200008392334], [1511842182.576814, 460, 0.9560999870300293], [1511842183.319649, 470, 0.9588000178337097], [1511842184.080669, 480, 0.9555000066757202], [1511842184.877782, 490, 0.955299973487854], [1511842185.816123, 500, 0.9545000195503235], [1511842186.704832, 510, 0.9559999704360962], [1511842187.475836, 520, 0.9563999772071838], [1511842188.231245, 530, 0.9596999883651733], [1511842188.981693, 540, 0.9562000036239624], [1511842189.72966, 550, 0.9553999900817871], [1511842190.468321, 560, 0.9580000042915344], [1511842191.281197, 570, 0.9575999975204468], [1511842192.046349, 580, 0.9589999914169312], [1511842192.808289, 590, 0.9599999785423279], [1511842193.623127, 600, 0.9603999853134155], [1511842194.371868, 610, 0.9605000019073486], [1511842195.257612, 620, 0.9606000185012817], [1511842196.113539, 630, 0.9559999704360962], [1511842196.914175, 640, 0.9610000252723694], [1511842197.701817, 650, 0.9621999859809875], [1511842198.457409, 660, 0.9599000215530396], [1511842199.228452, 670, 0.9620000123977661], [1511842199.973789, 680, 0.9628000259399414], [1511842200.786072, 690, 0.963699996471405], [1511842201.752806, 700, 0.963100016117096], [1511842202.55644, 710, 0.9632999897003174], [1511842203.355084, 720, 0.964900016784668], [1511842204.150081, 730, 0.9638000130653381], [1511842204.931775, 740, 0.9660000205039978], [1511842205.878417, 750, 0.9628000259399414], [1511842206.808438, 760, 0.9613000154495239], [1511842207.560386, 770, 0.9656000137329102], [1511842208.364481, 780, 0.9664000272750854], [1511842209.154627, 790, 0.9628000259399414], [1511842210.098208, 800, 0.9627000093460083], [1511842210.969653, 810, 0.9656999707221985], [1511842211.929992, 820, 0.9657999873161316], [1511842212.882522, 830, 0.9656000137329102], [1511842213.690861, 840, 0.9682000279426575], [1511842214.508996, 850, 0.9678999781608582], [1511842215.295672, 860, 0.968999981880188], [1511842216.096151, 870, 0.9682999849319458], [1511842216.910752, 880, 0.9661999940872192], [1511842217.718586, 890, 0.9648000001907349], [1511842218.653347, 900, 0.9678000211715698], [1511842219.421863, 910, 0.96670001745224], [1511842220.34967, 920, 0.9674000144004822], [1511842221.234565, 930, 0.9684000015258789], [1511842222.075741, 940, 0.9682000279426575], [1511842222.854794, 950, 0.9663000106811523], [1511842223.658847, 960, 0.9681000113487244], [1511842224.500655, 970, 0.9692999720573425], [1511842225.308939, 980, 0.9692000150680542], [1511842226.065685, 990, 0.9692000150680542]]
}
}
}
};
/**
* get mock data
*
* @param {string} path request path
* @param {Object} queryParam query params
* @param {Object} postParam post params
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
return {
// moock delay
_timeout: 0,
// mock http status
_status: 200,
// mock response data
_data: {
status: 0,
msg: 'SUCCESS',
data: {
"test": {
"layer2/biases/summaries/mean": {
"displayName": "layer2/biases/summaries/mean",
"description": ""
},
"layer1/weights/summaries/min": {
"displayName": "layer1/weights/summaries/min",
"description": ""
},
"layer2/biases/summaries/stddev_1": {
"displayName": "layer2/biases/summaries/stddev_1",
"description": ""
},
"layer1/biases/summaries/mean": {
"displayName": "layer1/biases/summaries/mean",
"description": ""
},
"layer1/weights/summaries/mean": {
"displayName": "layer1/weights/summaries/mean",
"description": ""
},
"dropout/dropout_keep_probability": {
"displayName": "dropout/dropout_keep_probability",
"description": ""
},
"layer1/weights/summaries/max": {
"displayName": "layer1/weights/summaries/max",
"description": ""
}, "layer2/weights/summaries/mean": {
"displayName": "layer2/weights/summaries/mean",
"description": ""
}, "layer2/weights/summaries/stddev_1": {
"displayName": "layer2/weights/summaries/stddev_1",
"description": ""
},
"layer2/weights/summaries/min": {
"displayName": "layer2/weights/summaries/min",
"description": ""
},
"layer1/biases/summaries/max": {
"displayName": "layer1/biases/summaries/max",
"description": ""
},
"layer2/weights/summaries/max": {
"displayName": "layer2/weights/summaries/max",
"description": ""
},
"accuracy_1": {
"displayName": "accuracy_1",
"description": ""
},
"layer2/biases/summaries/min": {
"displayName": "layer2/biases/summaries/min",
"description": ""
},
"cross_entropy_1": {
"displayName": "cross_entropy_1",
"description": ""
},
"layer2/biases/summaries/max": {
"displayName": "layer2/biases/summaries/max",
"description": ""
},
"layer1/weights/summaries/stddev_1": {
"displayName": "layer1/weights/summaries/stddev_1",
"description": ""
},
"layer1/biases/summaries/stddev_1": {
"displayName": "layer1/biases/summaries/stddev_1",
"description": ""
},
"layer1/biases/summaries/min": {
"displayName": "layer1/biases/summaries/min",
"description": ""
}
},
"train": {
"layer2/biases/summaries/mean": {
"displayName": "layer2/biases/summaries/mean",
"description": ""
},
"layer1/weights/summaries/min": {
"displayName": "layer1/weights/summaries/min",
"description": ""
},
"layer2/biases/summaries/stddev_1": {
"displayName": "layer2/biases/summaries/stddev_1",
"description": ""
},
"layer1/biases/summaries/mean": {
"displayName": "layer1/biases/summaries/mean",
"description": ""
},
"layer1/weights/summaries/mean": {
"displayName": "layer1/weights/summaries/mean",
"description": ""
},
"dropout/dropout_keep_probability": {
"displayName": "dropout/dropout_keep_probability",
"description": ""
},
"layer1/weights/summaries/max": {
"displayName": "layer1/weights/summaries/max",
"description": ""
},
"layer2/weights/summaries/mean": {
"displayName": "layer2/weights/summaries/mean",
"description": ""
},
"layer2/weights/summaries/stddev_1": {
"displayName": "layer2/weights/summaries/stddev_1",
"description": ""
},
"layer2/weights/summaries/min": {
"displayName": "layer2/weights/summaries/min",
"description": ""
},
"layer1/biases/summaries/max": {
"displayName": "layer1/biases/summaries/max",
"description": ""
},
"layer2/weights/summaries/max": {
"displayName": "layer2/weights/summaries/max",
"description": ""
},
"accuracy_1": {
"displayName": "accuracy_1",
"description": ""
},
"layer2/biases/summaries/min": {
"displayName": "layer2/biases/summaries/min",
"description": ""
},
"cross_entropy_1": {
"displayName": "cross_entropy_1",
"description": ""
},
"layer2/biases/summaries/max": {
"displayName": "layer2/biases/summaries/max",
"description": ""
},
"layer1/weights/summaries/stddev_1": {
"displayName": "layer1/weights/summaries/stddev_1",
"description": ""
},
"layer1/biases/summaries/stddev_1": {
"displayName": "layer1/biases/summaries/stddev_1",
"description": ""
},
"layer1/biases/summaries/min": {
"displayName": "layer1/biases/summaries/min",
"description": ""
}
}
}
}
};
};
/**
* get mock data
*
* @param {string} path request path
* @param {Object} queryParam query params
* @param {Object} postParam post params
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
return {
// moock delay
_timeout: 0,
// mock http status
_status: 200,
// mock response data
_data: {
status: 0,
msg: 'SUCCESS',
data: ["train", "test", "model"]
}
};
};
{
"name": "front",
"version": "1.0.1",
"description": "VisualDL frontend",
"author": "ecomfe",
"private": true,
"scripts": {
"release": "cross-env NODE_ENV=production node ./tool/build.js",
"build": "cross-env NODE_ENV=dev node ./tool/build.js",
"dev": "cross-env NODE_ENV=dev node tool/dev-server.js",
"lint": "./node_modules/fecs/bin/fecs --rule",
"precommit": "npm run lint",
"prepush": "npm run lint"
},
"engines": {
"node": ">= 6.4.0"
},
"dependencies": {
"axios": "^0.16.1",
"echarts": "^3.8.5",
"file-saver": "^1.3.3",
"lodash": "^4.17.4",
"normalize.css": "^6.0.0",
"qs": "^6.5.1",
"san": "3.2.3",
"san-mui": "^1.0.4",
"san-router": "^1.1.1",
"san-store": "^1.0.1",
"san-update": "^2.1.0",
"xlsx": "^0.11.3"
},
"devDependencies": {
"autoprefixer": "^6.7.7",
"autoresponse": "^0.2.0",
"babel-core": "^6.25.0",
"babel-loader": "^6.2.7",
"babel-plugin-transform-class-properties": "^6.19.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-0": "^6.16.0",
"babel-register": "^6.0.0",
"babel-runtime": "^6.26.0",
"case-sensitive-paths-webpack-plugin": "^2.1.1",
"chalk": "^1.1.3",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^4.0.0",
"css-loader": "^0.28.0",
"express": "^4.16.2",
"extract-text-webpack-plugin": "^2.1.0",
"fecs": "^1.5.3",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.6.1",
"glob": "^7.1.1",
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.4",
"husky": "^0.14.3",
"json-loader": "^0.5.4",
"opn": "^5.1.0",
"optimize-css-assets-webpack-plugin": "^1.3.2",
"ora": "^1.1.0",
"postcss-loader": "^1.3.3",
"raw-loader": "^0.5.1",
"rimraf": "^2.6.2",
"san-loader": "^0.0.4",
"style-loader": "^0.16.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"url-loader": "^0.5.8",
"webpack": "^2.4.1",
"webpack-dev-middleware": "^1.12.1",
"webpack-dev-server": "^2.4.2",
"webpack-hot-middleware": "^2.19.1",
"webpack-merge": "^4.1.0",
"yargs": "^8.0.2"
}
}
<template>
<div id="app">
<ui-app-menu
on-item-click="menuChange($event)"
></ui-app-menu>
<div id="content-container" class="visual-dl-content-container">
<div id="app-content" class="visual-dl-app-content">
<div id="content"></div>
</div>
</div>
</div>
</template>
<script>
import AppMenu from './common/ui/AppMenu';
import {router} from 'san-router';
import {routeTo} from './common/util/routeTo';
export default {
components: {
'ui-app-menu': AppMenu
},
initData() {
return {};
},
attached() {
router.start();
},
menuChange({value, url, title}) {
routeTo(url);
}
};
</script>
<style lang="stylus">
// @import './style/variables';
// +prefix-classes(prefix)
</style>
<template>
<div class="visual-dl-app-menu">
<san-appbar title="VisualDL">
<san-menu slot="right">
<san-menu-item
san-for="item in items"
class="{{selected === item.value ? 'sm-menu-item-selected' : ''}}"
on-click="handleItemClick(item)"
title="{{item.title}}" />
</san-menu>
</san-appbar>
</div>
</template>
<script>
import Appbar from 'san-mui/AppBar';
import {MenuItem, Menu} from 'san-mui/Menu';
export default {
components: {
'san-appbar': Appbar,
'san-menu': Menu,
'san-menu-item': MenuItem
},
initData() {
return {
selected: '1',
items: [
{
value: '1',
url: '/scalars',
title: 'SCALARS'
},
{
value: '2',
url: '/image',
title: 'IMAGES'
}
]
};
},
handleItemClick(item) {
this.data.set('selected', item.value);
this.fire('item-click', item);
}
};
</script>
<style lang="stylus">
@import '../../style/variables';
+prefix-classes(prefix)
.app-menu
width 100%
.visual-dl-app-menu
.sm-appbar-title
flex none
.sm-appbar-right
width 100%
.sm-menu
width 100%
height 100%
display flex
flex-direction row
.sm-menu-item-selected
background #e4e4e4
</style>
<template>
<div class="visual-dl-charts">
<div class="visual-dl-chart-box" style="{{computedStyle}}">
</div>
<div class="visual-dl-chart-actions">
<ui-dropdown-menu
hintText="download type"
items="{{runsItems}}"
value="{=downloadType=}"
/>
<san-button on-click="handleDownLoad">
<san-icon>file_download</san-icon>
</san-button>
</div>
</div>
</template>
<script>
import Button from 'san-mui/Button';
import Icon from 'san-mui/Icon';
import DropDownMenu from '../../../common/ui/DropDownMenu';
import {generateJsonAndDownload} from '../../../common/util/downLoadFile';
import echarts from 'echarts';
import axios from 'axios';
import {getPluginScalarsScalars} from '../../../service';
export default {
components: {
'ui-dropdown-menu': DropDownMenu,
'san-button': Button,
'san-icon': Icon
},
computed: {
computedStyle() {
let width = this.data.get('width');
let height = this.data.get('height');
return 'height:' + height + 'px;'
+ 'width:' + width + 'px;';
}
},
initData() {
return {
width: 400,
height: 300,
// line config
options: {},
data: [
{
name: 'train',
value: []
}
],
// choose run type for download file
downloadType: ''
};
},
inited() {
this.watch('runsItems', val => {
this.initDownloadType();
});
},
attached() {
let tagInfo = this.data.get('tagInfo');
this.initCharts(tagInfo);
},
initDownloadType() {
let runsItems = this.data.get('runsItems') || [];
if (runsItems.length === 0) {
return;
}
this.data.set('downloadType', runsItems.find((item, index) => index === 0).value);
},
initCharts(tagInfo) {
this.createChart();
this.setChartsOptions(tagInfo);
this.setChartsData(tagInfo);
},
createChart() {
let el = this.el.getElementsByClassName('visual-dl-chart-box')[0];
this.myChart = echarts.init(el);
},
setChartsOptions({tagList, tag}) {
let seriesOption = tagList.map(item => {
return {
name: item.run,
type: 'line',
showSymbol: false,
hoverAnimation: false,
data: [],
smooth: true
};
});
let legendOptions = tagList.map(item => item.run);
let option = {
title: {
text: tag,
textStyle: {
fontSize: '12'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false
},
position: [10, 300]
},
toolbox: {
show: true,
showTitle: true,
feature: {
dataZoom: {},
restore: {},
saveAsImage: {}
},
left: 40,
top: 270
},
legend: {
data: legendOptions,
top: 30
},
grid: {
top: 70,
bottom: 50
},
xAxis: [
{
type: 'value',
boundaryGap: false
},
{
type: 'value',
boundaryGap: false
}
],
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
min(value) {
return value.min;
},
max(value) {
return value.max;
},
axisLabel: {
formatter(value, index) {
// keep 0.11111 to 0.111
return value.toString().slice(0, 5);
}
}
},
series: seriesOption
};
this.myChart.setOption(option);
},
setChartsData({tagList, tag}) {
let requestList = tagList.map(item => {
let params = {
run: item.run,
tag: tag
};
return getPluginScalarsScalars(params);
});
axios.all(requestList).then(resArray => {
let seriesData = resArray.map(res => {
return {
data: res.data,
encode: {
// map 1 to xAixs。
x: [1],
// map 2 to yAixs。
y: [2]
}
};
});
this.myChart.setOption({
series: seriesData
});
});
},
getChartOptions() {
return this.myChart.getOption() || {};
},
handleDownLoad() {
let options = this.getChartOptions();
let series = options.series || [];
let downloadType = this.data.get('downloadType');
let sery = series.find(item => item.name === downloadType) || {};
let tagInfo = this.data.get('tagInfo');
let fileName = tagInfo.tag.replace(/\//g, '-');
generateJsonAndDownload(sery.data, fileName);
}
};
</script>
<style lang="stylus">
.visual-dl-charts
float left
.visual-dl-chart-actions
.sm-form-item
width 300px
display inline-block
</style>
<template>
<div class="sm-form-item">
<label class="label">{{label}}</label>
<div class="group-box">
<san-checkbox
san-if="showAll"
class="checkbox-all"
label="全选"
value="all"
on-change="handleAllChange($event)"
checked="{{All}}"
></san-checkbox>
<div class="san-form-check-group">
<san-checkbox
san-for="item in items"
label="{{item.name}}"
value="{{item.value}}"
disabled="{{item.disabled}}"
checked="{{value}}"
on-change="handleChange($event, item)"
></san-checkbox>
</div>
</div>
</div>
</template>
<script>
import Checkbox from 'san-mui/Checkbox';
import {DataTypes} from 'san';
export default {
components: {
'san-checkbox': Checkbox
},
dataTypes: {
items: DataTypes.array,
value: DataTypes.array
},
initData() {
return {
allValue: [],
items: {},
value: [],
All: [],
showAll: false
};
},
computed: {
allValue() {
let items = this.data.get('items') || [];
return items.map(item => item.value);
}
},
attached() {
this.jugdeAll();
this.watch('value', val => {
this.dispatch('UI:form-item-change');
this.jugdeAll();
});
},
handleChange(e, item) {
let checked = e.target.checked;
let valueItem = item.value;
if (checked) {
this.addValue(valueItem);
}
else {
this.removeValue(valueItem);
}
this.dispatch('UI:form-item-change');
this.jugdeAll();
},
handleAllChange(e) {
let checked = e.target.checked;
let allValue = this.data.get('allValue') || [];
if (checked) {
this.data.set('value', allValue.slice());
this.fire('valueChange', allValue.slice());
}
else {
this.data.set('value', []);
this.fire('valueChange', []);
}
this.dispatch('UI:form-item-change');
},
addValue(itemValue) {
let value = this.data.get('value') || [];
if (!value.includes(itemValue)) {
value.push(itemValue);
this.data.set('value', value.slice());
this.fire('valueChange', value.slice());
}
},
removeValue(itemValue) {
let value = this.data.get('value') || [];
if (value.includes(itemValue)) {
let index = value.indexOf(itemValue);
value.splice(index, 1);
this.data.set('value', value.slice());
this.fire('valueChange', value.slice());
}
},
jugdeAll() {
let allValue = this.data.get('allValue') || [];
let value = this.data.get('value') || [];
let isAll = allValue.every(val => value.includes(val));
if (isAll) {
this.data.set('All', ['all']);
}
else {
this.data.set('All', []);
}
}
};
</script>
<template>
<div class="sm-form-item">
<label class="label">{{label}}</label>
<san-drop-down-menu
error="{{error}}"
disabled="{{disabled}}"
value="{=value=}"
maxHeight="{{200}}"
autoWidth="{{false}}">
<san-menu-item
s-for="item in items"
on-change="menuItemChange(item)"
value="{{item.value}}"
label="{{item.name}}" />
</sm-drop-down-menu>
</div>
</template>
<script>
import {DataTypes} from 'san';
import {MenuItem, DropDownMenu} from 'san-mui/Menu';
export default {
components: {
'san-menu-item': MenuItem,
'san-drop-down-menu': DropDownMenu
},
dataTypes: {
value: DataTypes.string,
items: DataTypes.array,
disabled: DataTypes.bool
},
menuItemChange(item) {
let value = item.value;
this.fire('valueChange', value);
this.dispatch('UI:form-item-change');
},
initData() {
return {
value: '',
items: []
};
}
};
</script>
<template>
<div class="visaul-dl-expand-panel">
<h3
class="visaul-dl-expand-head"
on-click="handleHeadClick()"
>
<span>{{title}}</span>
</h3>
<div
style="display:{{displayStyle}}"
class="visaul-dl-expand-panel-content"
>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
computed: {
displayStyle() {
let isShow = this.data.get('isShow');
return isShow ? 'block' : 'none';
}
},
initData() {
return {
isShow: false
};
},
handleHeadClick() {
this.toogleShow();
},
toogleShow() {
let isShow = this.data.get('isShow');
this.data.set('isShow', !isShow);
}
};
</script>
<style lang="stylus">
.visaul-dl-expand-panel
.visaul-dl-expand-head
border solid 1px #e4e4e4
line-height 50px
height 50px
padding 0 20px
cursor pointer
.visaul-dl-expand-panel-content
padding 0 20px
.visaul-dl-expand-panel-content:after
content: "";
clear: both;
display: block;
</style>
import NotificationItem from './NotificationItem';
let seed = 1;
// instances
const instances = [];
/**
* caculate top dist
*
* @param {number} topDist top dist
* @return {number} final top dist
*/
function calcTopDist(topDist = 16) {
for (let i = 0, len = instances.length; i < len; i++) {
topDist += (instances[i].vm.el.offsetHeight + 16);
}
return topDist;
}
/**
* Notification main func
*
* @param {Object} options options
* @return {NotificationItem} instance
*/
const notification = function (options = {}) {
options.top = calcTopDist(options.offset);
const {onClose, onClick} = options;
delete options.onClick;
delete options.onClose;
delete options.offset;
const instance = {
vm: new NotificationItem({
data: options
}),
id: `notification_${seed++}`
};
if (typeof onClick === 'function') {
instance.vm.on('itemClick', onClick);
}
instance.vm.on('close', () => {
notification.close(instance.id, onClose);
});
instance.vm.attach(document.body);
instances.push(instance);
return instance.vm;
};
/**
* close
*
* @param {string} id instance id
* @param {Function} onClose cusmtom func
*/
notification.close = function (id, onClose) {
let index;
let removedHeight;
let len = instances.length;
for (let i = 0; i < len; i++) {
if (id === instances[i].id) {
if (typeof onClose === 'function') {
onClose(instances[i]);
}
index = i;
removedHeight = instances[i].vm.el.offsetHeight;
// distroy instance
instances[i].vm.dispose();
instances[i] = null;
// reomve instance fron instances
instances.splice(i, 1);
break;
}
}
// change the left notification's height
if (len > 1) {
for (let i = index; i < len - 1; i++) {
instances[i].vm.el.style.top = `${parseInt(instances[i].vm.el.style.top, 10) - removedHeight - 16}px`;
}
}
};
// fout type func
['success', 'warning', 'info', 'error'].forEach(type => {
notification[type] = options => {
if (typeof options === 'string') {
options = {
message: options
};
}
options = options || {};
options.type = type;
return notification(options);
};
});
export default notification;
## Notification
### base usage
```san Notification
<template>
<div>
<san-button
variants="raised info"
on-click="handleClick01">
auto close
</san-button>
<san-button
variants="raised info"
on-click="handleClick02">
no auto close
</san-button>
</div>
</template>
<script>
import Notification from '../src/Notification';
import '../src/Notification/Notification.styl';
import Button from '../src/Button';
import '../src/Button/Button.styl';
export default {
components: {
'san-button': Button
},
// auto close
handleClick01() {
Notification({
message: 'welcome',
title: 'success'
})
},
// no auto close
handleClick02() {
Notification({
message: 'welcome',
title: 'success',
duration: 0
})
}
}
</script>
```
### with icon
```san Notification
<template>
<div>
<san-button
variants="raised info"
on-click="handleClick01">
success
</san-button>
<san-button
variants="raised info"
on-click="handleClick02">
warning
</san-button>
<san-button
variants="raised info"
on-click="handleClick03">
info
</san-button>
<san-button
variants="raised info"
on-click="handleClick04">
error
</san-button>
<san-button
variants="raised info"
on-click="handleClick05">
simplify
</san-button>
</div>
</template>
<script>
import Notification from '../src/Notification';
import '../src/Notification/Notification.styl';
import Button from '../src/Button';
import '../src/Button/Button.styl';
export default {
components: {
'san-button': Button
},
handleClick01() {
Notification({
message: 'welcome',
title: 'success',
type: 'success'
})
},
handleClick02() {
Notification({
message: 'warning',
title: 'warning',
type: 'warning'
})
},
handleClick03() {
Notification({
message: 'info',
title: 'info',
type: 'info'
})
},
handleClick04() {
Notification({
message: '不可以攻击己方水晶',
title: '错误',
type: 'error'
})
},
handleClick05() {
Notification.success('simplify')
}
}
</script>
```
### width offset
```san Notification
<template>
<div>
<san-button
variants="raised info"
on-click="handleClick01">
width offset
</san-button>
</div>
</template>
<script>
import Notification from '../src/Notification';
import '../src/Notification/Notification.styl';
import Button from '../src/Button';
import '../src/Button/Button.styl';
export default {
components: {
'san-button': Button
},
handleClick01() {
Notification({
message: 'offset 100px',
title: 'success',
offset:100
})
}
}
</script>
```
## API
### Props
| name | type | fefault | desc |
| --- | --- | --- | --- |
| title | String | | title |
| message | String | | content |
| customClass |String| | custom class |
| offset | Number | | offset to the top |
| onClick | Function | | on-click callback |
| onClose | Function | | on-close callback |
| duration | Number | 3000 | duration |
| type | String | | include success,error,warning,info, others not use |
.sm-notification
position: fixed
display: flex
z-index: 9999
right: 16px
top 16px
width: 330px
padding: 20px
box-sizing border-box
border-radius 2px
background-color #fff
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
transition: all .4s;
overflow: hidden
&.state-hidden
transform translateX(346px)
&.state-open
transform translateX(0)
&-group
color: #000
&-type
margin auto 0
margin-right 20px
&.type-success
color: #13ce66
&.type-info
color: #50bfff
&.type-warning
color: #f7ba2a
&.type-error
color: #ff4949
&-title
color: #1f2d3d
font-size 16px
&-content
color: #8d9baf
padding: 5px 0
&-close
position: absolute
cursor: pointer
right 20px
top: 20px
&-btn
transition all .3s
color #bfcbd9
&:hover
color: #777F8C
import {Component} from 'san';
import Icon from 'san-mui/Icon';
const typeMap = {
success: 'check_circle',
info: 'info',
warning: 'error',
error: 'cancel'
};
export default class NotificationItem extends Component {
static template = `
<div
class="sm-notification {{customClass}} {{closed ? 'state-hidden' : 'state-open'}}"
style="top: {{top}}px"
on-mouseenter="clearTimer"
on-mouseleave="startTimer"
on-click="handleClick">
<san-icon
san-if="type"
class="sm-notification-type type-{{type}}"
size="50">{{iconType}}</san-icon>
<div class="sm-notification-group" >
<p class="sm-notification-title">{{title}}</p>
<div class="sm-notification-content">{{message}}</div>
<div on-click="close($event)" class="sm-notification-close">
<san-icon size="20" class="sm-notification-close-btn">close</san-icon>
</div>
</div>
</div>
`;
initData() {
return {
title: '',
message: '',
duration: 3000,
closed: true
};
}
static components = {
'san-icon': Icon
};
static computed = {
iconType() {
return typeMap[this.data.get('type')];
}
};
handleClick() {
this.fire('itemClick');
}
/**
* close
*
* @param {Object} e event Object
*/
close(e) {
if (e) {
e.stopPropagation();
}
this.data.set('closed', true);
this.el.addEventListener('transitionend', () => {
this.fire('close');
});
}
/**
* clear timer
*/
clearTimer() {
clearTimeout(this.timer);
}
/**
* start timet
*/
startTimer() {
const duration = this.data.get('duration');
if (duration > 0) {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close();
}
}, duration);
}
}
attached() {
if (this.el.parentNode !== document.body) {
document.body.appendChild(this.el);
}
requestAnimationFrame(() => {
this.data.set('closed', false);
});
this.startTimer();
}
detached() {
this.clearTimer();
}
}
import Notification from './Notification';
import NotificationItem from './NotificationItem';
export {
NotificationItem,
Notification as default
};
<template>
<div class="sm-form-item">
<label class="label">{{label}}</label>
<div class="group-box">
<san-radio
on-change="handleChange($event)"
san-for="item in items"
label="{{item.name}}"
value="{{item.value}}"
disabled="{{item.disabled}}"
checked="{=value=}"
></san-radio>
</div>
</div>
</template>
<script>
import Radio from 'san-mui/Radio';
import {DataTypes} from 'san';
export default {
components: {
'san-radio': Radio
},
dataTypes: {
items: DataTypes.array,
value: DataTypes.string
},
initData() {
return {
items: {},
value: [],
label: ''
};
},
attached(value) {
this.watch('value', val => {
this.dispatch('UI:form-item-change', val);
});
},
handleChange(val) {
this.fire('valueChange', val);
}
};
</script>
\ No newline at end of file
<template>
<div class="sm-form-item">
<label class="label">{{label}}</label>
<div class="input-box">
<san-slider
on-change="handleSlideChange($event)"
value="{{value}}"
min="{{min}}"
max="{{max}}"
step="{{step}}"
/>
<san-input-number
min="{{min}}"
max="{{max}}"
step="{{step}}"
value="{=value=}"
on-change="handlerChange($event)"
size="small">
</san-input-number>
</div>
</div>
</template>
<script>
import Slider from 'san-mui/Slider';
import InputNumber from 'san-mui/InputNumber';
export default {
components: {
'san-slider': Slider,
'san-input-number': InputNumber
},
initData() {
return {
value: '0',
label: '',
max: 0,
min: 0,
step: 0
};
},
handleSlideChange(val) {
this.data.set('value', val.toString());
}
};
</script>
<style lang="stylus">
.sm-form-item
.input-box
margin-top 10px
.sm-slider
display inline-block
margin-right 20px
margin-top 4px
float left
width 100px
.sm-slider-thumb
transform translate(0, -50%)
.sm-inputNumber
width 130px
display inline-block
</style>
// initual style
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; } body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; } h1, h2, h3, h4, h5, h6{ font-size:100%; } address, cite, dfn, em, var { font-style:normal; } code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:12px; } ul, ol { list-style:none; } a { text-decoration:none; } a:hover { text-decoration:underline; } sup { vertical-align:text-top; } sub{ vertical-align:text-bottom; } legend { color:#000; } fieldset, img { border:0; } button, input, select, textarea { font-size:100%; } table { border-collapse:collapse; border-spacing:0; }
@import './Notification/Notification.styl'
// modify some styles of san-mui
.sm-icon
width 100%
height 100%
overflow hidden
.sm-radio,
.sm-checkbox
margin-top 2px
margin-bottom 4px
margin-right 10px
float left
width 100%
.sm-radio-wrapper,
.sm-checkbox-wrapper
justify-content start
.sm-radio-label,
.sm-checkbox-label
font-size 12px
.sm-radio-icon
.sm-checkbox-icon
width 20px
height 20px
margin-right 10px
.sm-radio-ripple-wrapper
.sm-checkbox-ripple-wrapper
width 20px
height 20px
top 0px
left 0px
.sm-checkbox-svg-icon,
.sm-radio-svg-icon
width 20px
height 20px
.sm-checkbox-icon-uncheck
.sm-radio-icon-uncheck
color #58666e
.sm-radio-label,
.sm-checkbox-label
color #58666e
.sm-dropdown-menu
.sm-dropdown-menu-icon
.sm-icon
color #58666e
.sm-popover-content
.sm-menu-item
line-height 30px
font-size 14px
color #58666e
.sm-menu-item.state-selected
color #ff4081
.sm-text-field-input
color #58666e
.sm-form-item
margin-top 10px
.label
color rgba(0,0,0,0.54)
font-size 12px
line-height 20px
.group-box
overflow hidden
.collapse-transition
transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;
.sm-pagination
margin 30px 0
font-size 12px
\ No newline at end of file
import XLSX from 'xlsx';
import FileSaver from 'file-saver';
const aoaToSheet = XLSX.utils.aoa_to_sheet;
const saveAs = FileSaver.saveAs;
function s2ab(s) {
if (typeof ArrayBuffer !== 'undefined') {
let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf);
for (let i = 0; i !== s.length; ++i) {
view[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}
let buf = new Array(s.length);
for (let i = 0; i !== s.length; ++i) {
buf[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}
/**
* download Excel
*
* @desc transform data like [['A', 'B', 'C'], ['1', '2', '3'],[['1-1', '2-1', '3-1']]] to xlsx and download
* @param {Array} data the data for the xlsx
* @param {string} name filename
*/
export const generateXLSXandAutoDownload = function (data, name) {
let wopts = {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
};
let ws = aoaToSheet(data);
let wb = {
SheetNames: ['Export'],
Sheets: {},
Props: {}
};
wb.Sheets.Export = ws;
let wbout = XLSX.write(wb, wopts);
saveAs(
new Blob(
[s2ab(wbout)],
{
type: 'application/octet-stream'
}
),
name + '.xlsx' || 'sheetjs.xlsx'
);
};
/**
* download json
*
* @desc download json
* @param {Array} data the data for the xlsx
* @param {string} name filename
*/
export const generateJsonAndDownload = function (data, name) {
saveAs(
new Blob(
[s2ab(JSON.stringify(data, null, ' '))],
{
type: 'application/octet-stream'
}
),
name + '.json' || 'json.json'
);
};
import axios from 'axios';
import qs from 'qs';
import Notification from '../ui/Notification';
const STATUS = 'status';
const STATUSINFO = 'msg';
const instance = axios.create({
baseURL: '/',
timeout: 30000
});
const responseErrorStatus = response => {
const data = response.data;
if (data[STATUS] !== 0) {
Notification.error(data[STATUSINFO]);
return Promise.reject(data);
}
return data;
};
const responseNetError = error => {
Notification.error('net error');
return Promise.reject(error);
};
// post from
const formInstance = axios.create({
baseURL: '/',
timeout: 3000,
transformRequest: [data => qs.stringify(data)],
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json,application/vnd.ms-excel'
}
});
formInstance.interceptors.response.use(responseErrorStatus, responseNetError);
instance.interceptors.response.use(responseErrorStatus, responseNetError);
export const makeService = (url, opt = {method: 'get'}) => (params = {}) => {
if (opt.method === 'delete' || opt.method === 'get') {
params = {params};
}
return instance[opt.method](url, params);
};
export const makeFormService = (url, method = 'post') => (params = {}) => formInstance[method](url, params);
import {router} from 'san-router';
export function routeTo(url, params = {}) {
let paramsArr = Object.keys(params).map(key => `${key}=${params[key]}`);
let urlParams = (url.indexOf('?') > -1 ? '&' : '?') + paramsArr.join('&');
router.locator.redirect(urlParams.length > 1 ? `${url}${urlParams}` : url);
}
<template>
<article>
<h1>
{{text}}
</h1>
</article>
</template>
<script>
export default {
initData() {
return {
text: 'welcome'
};
}
};
</script>
import {router} from 'san-router';
import HomePage from './Home';
router.add({
target: '#content',
rule: '/',
Component: HomePage
});
import 'normalize.css/normalize.css';
import 'san-mui/index.css';
import './common/ui/ui-common.styl';
import './home/index';
import './scalars/index';
import App from './App';
new App({
data: {
titleName: 'VisualDL'
}
}).attach(document.getElementById('root'));
<template>
<div class="visual-dl-scalar-container">
<div class="visual-dl-scalar-left">
<div class="visual-dl-scalar-config-container">
<ui-config
runsItems="{{runsItems}}"
config="{=config=}"
></ui-config>
</div>
</div>
<div class="visual-dl-scalar-right">
<ui-chart-page
config="{{config}}"
runsItems="{{runsItems}}"
tagList="{{filteredTagsList}}"
title="Tags matching {{config.groupNameReg}}"
></ui-chart-page>
<ui-chart-page
san-for="item in groupedTags"
config="{{config}}"
runsItems="{{runsItems}}"
tagList="{{item.tags}}"
title="{{item.group}}"
></ui-chart-page>
</div>
</div>
</template>
<script>
import {getPluginScalarsTags, getRuns} from '../service';
import config from './ui/config';
import chartPage from './ui/chartPage';
import {debounce, flatten, uniq} from 'lodash';
export default {
components: {
'ui-config': config,
'ui-chart-page': chartPage
},
computed: {
runsItems() {
let runsArray = this.data.get('runsArray') || [];
return runsArray.map(item => {
return {
name: item,
value: item
};
});
},
tagsList() {
let tags = this.data.get('tags');
let runs = Object.keys(tags);
let tagsArray = runs.map(run => Object.keys(tags[run]));
let allUniqTags = uniq(flatten(tagsArray));
// get the data for every chart
return allUniqTags.map(tag => {
let tagList = runs.map(run => {
return {
run,
tag: tags[run][tag]
};
});
return {
tagList,
tag,
group: tag.split('/')[0]
};
});
},
groupedTags() {
let tagsList = this.data.get('tagsList') || [];
// put data in group
let groupData = {};
tagsList.forEach(item => {
let group = item.group;
if (groupData[group] === undefined) {
groupData[group] = [];
groupData[group].push(item);
}
else {
groupData[group].push(item);
}
});
// to array
let groups = Object.keys(groupData);
return groups.map(group => {
return {
group,
tags: groupData[group]
};
});
}
},
initData() {
return {
runsArray: [],
tags: [],
config: {
groupNameReg: '.*',
smoothing: '0.5',
horizontal: '1',
sortingMethod: '2',
link: [],
chart: []
}
};
},
inited() {
getPluginScalarsTags().then(({errno, data}) => {
this.data.set('tags', data);
// filter when inited
let groupNameReg = this.data.get('config.groupNameReg');
this.filterTagsList(groupNameReg);
});
getRuns().then(({errno, data}) => {
this.data.set('runsArray', data);
});
// need debounce, can't use computed
this.watch('config.groupNameReg', debounce(this.filterTagsList, 300));
},
filterTagsList(groupNameReg) {
let tagsList = this.data.get('tagsList') || [];
let regExp = new RegExp(groupNameReg);
let filtedTagsList = tagsList.filter(item => regExp.test(item.tag));
this.data.set('filteredTagsList', filtedTagsList);
}
};
</script>
<style lang="stylus">
@import '../style/variables';
+prefix-classes('visual-dl-scalar-')
.container
padding-left 300px
position relative
.left
width 280px
min-height 300px
border solid 1px #e4e4e4
position absolute
left 0
.right
width 100%
border solid 1px #e4e4e4
min-height 300px
padding 20px
</style>
import {router} from 'san-router';
import Scalar from './Scalars';
router.add({
target: '#content',
rule: '/scalars',
Component: Scalar
});
<template>
<div class="visaul-dl-chart-page">
<ui-expand-panel title="{{title}}">
<div class="visaul-dl-chart-box">
<ui-chart
san-for="tag in filteredTagList"
tagInfo="{{tag}}"
config="{{config}}"
runsItems="{{runsItems}}"
></ui-chart>
</div>
<ui-pagination
san-if="total > pageSize"
on-pageChange="handlePageChange($event)"
current="{{currentPage}}"
pageSize="{{pageSize}}"
total="{{total}}"
showSizeChanger="{{false}}"
/>
</ui-expand-panel>
</div>
</template>
<script>
import ExpandPanel from '../../common/ui/ExpandPanel';
import chart from '../../common/ui/Charts/chart';
import Pagination from 'san-mui/Pagination';
export default {
components: {
'ui-chart': chart,
'ui-expand-panel': ExpandPanel,
'ui-pagination': Pagination
},
computed: {
filteredTagList() {
let tagList = this.data.get('tagList') || [];
let currentPage = this.data.get('currentPage');
let pageSize = this.data.get('pageSize');
return tagList.slice((currentPage - 1) * pageSize, currentPage * pageSize);
},
total() {
let tagList = this.data.get('tagList') || [];
return tagList.length;
}
},
initData() {
return {
// current page
currentPage: 1,
// item per page
pageSize: 8
};
},
handlePageChange({pageNum}) {
this.data.set('currentPage', pageNum);
}
};
</script>
<style lang="stylus">
@import '../../style/variables';
+prefix-classes('visual-dl-')
.visaul-dl-chart-page
.visaul-dl-chart-box:after
content: "";
clear: both;
display: block;
</style>
<template>
<div class="visual-dl-scalar-config-com">
<san-text-field
hintText="input a tag group name to search"
label="Group name RegExp"
inputValue="{=config.groupNameReg=}"
/>
<ui-slider
label="Smoothing"
value="{=config.smoothing=}"
min="{{0}}"
max="{{1}}"
step="{{0.001}}"
/>
<ui-radio-group
label="Horizontal"
value="{{config.horizontal}}"
items="{{horizontalItems}}"
/>
<ui-dropdown-menu
label="Tooltip sorting method"
items="{{sortingMethodItems}}"
value="{=config.sortingMethod=}"
/>
<ui-checkbox-group
value="{=config.link=}"
items="{{lnksItems}}"
/>
<ui-checkbox-group
value="{=config.chart=}"
items="{{chartItems}}"
/>
<ui-checkbox-group
label="Runs"
items="{{runsItems}}"
/>
<san-button class="visual-dl-scalar-run-toggle" variants="raised secondery">Toggle All Runs</san-button>
</div>
</template>
<script>
import TextField from 'san-mui/TextField';
import Slider from '../../common/ui/Slider';
import RadioGroup from '../../common/ui/RadioGroup';
import DropDownMenu from '../../common/ui/DropDownMenu';
import CheckBoxGroup from '../../common/ui/CheckBoxGroup';
import Button from 'san-mui/Button';
export default {
components: {
'san-text-field': TextField,
'ui-slider': Slider,
'ui-radio-group': RadioGroup,
'ui-dropdown-menu': DropDownMenu,
'ui-checkbox-group': CheckBoxGroup,
'san-button': Button
},
initData() {
return {
config: {
groupName: 'aa',
smoothing: '0.5',
horizontal: '1',
sortingMethod: '2',
link: [],
chart: []
},
horizontalItems: [
{
name: 'Step',
value: '1'
},
{
name: 'Relative',
value: '2'
},
{
name: 'Wall',
value: '3'
}
],
sortingMethodItems: [
{
name: 'default',
value: '1'
},
{
name: 'descending',
value: '2'
},
{
name: 'ascending',
value: '3'
},
{
name: 'nearest',
value: '4'
}
],
runsItems: [],
lnksItems: [
{
value: '1',
name: 'Show data download links'
}
],
chartItems: [
{
value: '1',
name: 'Ignore outliers in chart scaling'
}
]
};
}
};
</script>
<style lang="stylus">
@import '../../style/variables';
+prefix-classes('visual-dl-scalar-')
.config-com
width 90%
margin 0 auto
.run-toggle
width 100%
margin-top 20px
</style>
import {makeService} from './common/util/http';
export const getPluginScalarsTags = makeService('/data/plugin/scalars/tags');
export const getRuns = makeService('/data/runs');
export const getPluginScalarsScalars = makeService('/data/plugin/scalars/scalars');
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>VisualDL</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
</head>
<body>
<div id="root"></div>
</body>
</html>
function noopReplace (val) { return val; }
function HtmlReplacePlugin(options) {
this.replacer = options.replacer || noopReplace;
}
HtmlReplacePlugin.prototype.apply = function(compiler) {
var replacer = this.replacer;
compiler.plugin('compilation', function(compilation) {
compilation.plugin('html-webpack-plugin-after-html-processing', function(htmlPluginData, callback) {
htmlPluginData.html = replacer(htmlPluginData.html, htmlPluginData);
callback(null, htmlPluginData);
});
});
};
module.exports = HtmlReplacePlugin;
const webpack = require('webpack');
const rm = require('rimraf');
const ora = require('ora');
const chalk = require('chalk');
const HtmlReplacePlugin = require('./HtmlReplacePlugin');
// env 'production'
process.env.WEBPACK_ENV = 'production';
let webpackConfig = require('./webpack.prod.config');
let spinner = ora('building for production...');
spinner.start();
let feRoots = {
'index': './'
};
webpackConfig.plugins = webpackConfig.plugins.concat([
new HtmlReplacePlugin({
replacer: function(html, opt) {
var name = opt.outputName.replace(/\.html$/, '');
var feRoot = feRoots[name];
if (feRoot) {
html = html
.replace(/href="/g, 'href="' + feRoot)
.replace(/src="/g, 'src="' + feRoot);
}
return html;
}
})
]);
rm(webpackConfig.output.path, err => {
if (err) throw err;
webpack(webpackConfig, function(err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n');
console.log(chalk.cyan(' Build complete.\n'));
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
));
})
});
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})
process.env.NODE_ENV = 'dev';
let devPort = 8999;
let opn = require('opn');
let express = require('express');
let webpack = require('webpack');
let proxyMiddleware = require('http-proxy-middleware');
let webpackConfig = require('./webpack.dev.config');
let autoresponse = require('autoresponse');
let path = require('path');
let port = devPort;
let autoOpenBrowser = false;
let app = express();
let compiler = webpack(webpackConfig);
let devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
disableHostCheck: true,
quiet: false,
noInfo: false,
stats: {
colors: true
},
headers: {'Access-Control-Allow-Origin': '*'}
});
let hotMiddleware = require('webpack-hot-middleware')(compiler, {
heartbeat: 2000
});
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({
action: 'reload'
});
cb();
});
});
// autoresponse
let AutoresponseMatchs = ['data'];
let matchsReg = new RegExp(AutoresponseMatchs.join('\|'));
let excludeReg = /\.(html|js|map)$/;
let isAutoresponseRequest = (path) => {
return !excludeReg.test(path) && matchsReg.test(path);
}
app.use(autoresponse({
logLevel: 'debug',
root: path.dirname(__dirname),
rules: [
{
match: isAutoresponseRequest,
method: ['get', 'post', , 'delete']
}
]
}));
// serve webpack bundle output
app.use(devMiddleware);
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware);
let uri = 'http://localhost:' + port;
let _resolve;
let readyPromise = new Promise(resolve => {
_resolve = resolve;
});
console.log('> Starting dev server...');
devMiddleware.waitUntilValid(() => {
console.log('> Listening at ' + uri + '\n');
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri);
}
_resolve();
});
let server = app.listen(port);
module.exports = {
ready: readyPromise,
close() {
server.close();
}
};
const path = require('path');
const projectPath = path.resolve(__dirname, '..');
const HtmlWebpackPlugin = require('html-webpack-plugin');
/**
* default apps
*
* @type {Array}
*/
let defaultApps = [
{
name: 'index',
feRoot: '/dist'
}
];
/**
* get entry js file
*
* @param {Array} apps appname
* @return {string} file path
*/
function getModules(apps) {
let modules = {};
apps.forEach(function (item) {
let app = item.name;
modules[app] = path.join(projectPath, 'src/' + app + '.js');
});
return modules;
}
/**
* get HtmlWebpackPlugin
*
* @param {string} app appname
* @param {boolan} template use template
* @return {HtmlWebpackPlugin} HtmlWebpackPlugin
*/
function getTemplate(app, template) {
let templateUrl = 'template/index.html';
if (template) {
templateUrl = `ejs-render-loader!template/${template}.ejs`;
}
return new HtmlWebpackPlugin({
filename: app + '.html',
template: templateUrl
});
}
/**
* get entry config
*
* @param {string} app appname
* @param {boolan} template use template
* @return {Object} config
*/
function getEntry(app, template) {
let buildApps = defaultApps.filter(function (item) {
let name = item.name;
return name === app;
});
buildApps = buildApps.length > 0 ? buildApps : defaultApps;
return {
module: getModules(buildApps),
template: buildApps.map(item => getTemplate(item.name, template))
};
}
module.exports.get = getEntry;
module.exports.entry = defaultApps;
const webpack = require('webpack');
const path = require('path');
const projectPath = path.resolve(__dirname, '..');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const argv = require('yargs').argv;
const isDev = process.env.NODE_ENV === 'dev';
const entry = require('./entry');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
function getLoaders(isDev, ext) {
let arr = ['css-loader'];
if (ext) {
arr.push(ext + '-loader');
}
if (isDev) {
arr.unshift('style-loader');
return arr;
}
return ExtractTextPlugin.extract({
use: arr,
fallback: 'style-loader'
});
}
/**
* entry config
*
* @type {Object}
*/
const ENTR_CONFIG = entry.get(argv.app, argv.template);
/**
* webpack config
*
* @type {Object}
*/
const config = {
entry: ENTR_CONFIG.module,
output: {
path: path.resolve(projectPath, 'dist'),
filename: '[name].[hash].js'
},
resolve: {
alias: {
'san-mui': 'san-mui/lib',
axios: 'axios/dist/axios.min.js'
},
extensions: ['.js', '.json', '.styl', '.css', '.html', '.san']
},
module: {
noParse: [
/node_modules\/(san|axios)\//
],
rules: [
{
test: /\.san$/,
loader: 'san-loader',
options: {
loaders: {
stylus: getLoaders(isDev, 'stylus')
}
}
},
{
test: /\.js$/,
exclude: /node_modules/,
include: [
path.resolve(projectPath, 'src')
],
loader: 'babel-loader'
},
{
test: /\.json$/,
loader: 'json-loader'
},
{
test: /\.html/,
loader: 'html-loader',
options: {
minimize: false
}
},
{
test: /\.css$/,
use: getLoaders(isDev)
},
{
test: /\.styl$/,
use: getLoaders(isDev, 'stylus')
},
{
test: /\.(gif|png|jpe?g)$/i,
loader: 'file-loader',
options: {
name: 'images/[name].[hash].[ext]'
}
},
{
test: /\.woff2?$/,
loader: 'url-loader',
options: {
name: 'fonts/[name].[hash].[ext]',
limit: '10000',
mimetype: 'application/font-woff'
}
},
{
test: /\.(ttf|eot|svg)$/,
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash].[ext]'
}
}
]
},
plugins: [
new CaseSensitivePathsPlugin(),
new webpack.LoaderOptionsPlugin({
test: /\.(styl|san)$/
})
]
};
// template config
config.plugins = config.plugins.concat(ENTR_CONFIG.template);
module.exports = config;
const webpack = require('webpack');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
let merge = require('webpack-merge');
let baseWebpackConfig = require('./webpack.config');
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./tool/dev-client'].concat(baseWebpackConfig.entry[name]);
});
/**
* dev config
*
* @type {Object}
*/
module.exports = merge(baseWebpackConfig, {
// cheap-module-eval-source-map is faster for development
devtool: '#cheap-module-eval-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"dev"'
}
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new FriendlyErrorsPlugin()
]
});
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const autoprefixer = require('autoprefixer');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
const bizCss = new ExtractTextPlugin('biz.[chunkhash].css');
let merge = require('webpack-merge');
let baseWebpackConfig = require('./webpack.config');
const autoPrefixOptions = {
browsers: [
'iOS >= 7',
'Android >= 4.0',
'ExplorerMobile >= 10',
'ie >= 9'
]
};
/**
* pro config
*
* @type {Object}
*/
module.exports = merge(baseWebpackConfig, {
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': 'production'
}
}),
new webpack.LoaderOptionsPlugin({
test: /\.(styl|san)$/,
san: {
autoprefixer: autoPrefixOptions
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.[chunkhash].js',
minChunks: function (module, count) {
const resPath = module.resource;
return resPath && /\.js$/.test(resPath)
&& resPath.indexOf(
path.join(__dirname, '../node_modules')
) === 0;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
'screw_ie8': true, // no ie6/7/8
'warnings': false
},
comments: false,
sourceMap: false
}),
bizCss,
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
})
]
});
from setuptools import setup
packages = ['visualdl', 'visualdl.frontend.dist', 'visualdl.mock']
setup(
name="visualdl",
version="0.0.1",
packages=packages,
package_data={'visualdl.frontend.dist': ['*', 'fonts/*']},
include_package_data=True,
install_requires=['flask>=0.12.1'],
url='http://www.baidu.com/',
license='Apache 2.0')
import log
__all__ = ['log']
import logging
logger = logging
logger.basicConfig(
format='[%(levelname)s %(asctime)s %(filename)s:%(lineno)s] %(message)s')
logger.getLogger().setLevel(logging.INFO)
def sequence_data():
return [[1465980660.726887, 1, 0.07000000029802322],
[1465980660.767164, 2, 0.18000000715255737],
[1465980660.799817, 3, 0.3199999928474426],
[1465980660.831853, 4, 0.33000001311302185],
[1465980660.86555, 5, 0.3400000035762787],
[1465980660.898716, 6, 0.6000000238418579],
[1465980660.930804, 7, 0.6299999952316284],
[1465980660.963156, 8, 0.6299999952316284],
[1465980660.995727, 9, 0.6299999952316284],
[1465980661.603699, 11, 0.75],
[1465980661.641232, 12, 0.7900000214576721],
[1465980661.674311, 13, 0.7099999785423279],
[1465980661.704281, 14, 0.7099999785423279],
[1465980661.737259, 15, 0.7200000286102295],
[1465980661.768047, 16, 0.75],
[1465980661.801236, 17, 0.8600000143051147],
[1465980661.832749, 18, 0.7799999713897705],
[1465980661.862822, 19, 0.8399999737739563],
[1465980662.481258, 21, 0.699999988079071],
[1465980662.521448, 22, 0.6700000166893005],
[1465980662.557197, 23, 0.7699999809265137],
[1465980662.593898, 24, 0.7900000214576721],
[1465980662.629991, 25, 0.7200000286102295],
[1465980662.671198, 26, 0.8100000023841858],
[1465980662.711186, 27, 0.7599999904632568],
[1465980662.750267, 28, 0.7799999713897705],
[1465980662.791909, 29, 0.8299999833106995],
[1465980663.47027, 31, 0.8100000023841858],
[1465980663.538732, 32, 0.8500000238418579],
[1465980663.57077, 33, 0.8600000143051147],
[1465980663.60126, 34, 0.8199999928474426],
[1465980663.631059, 35, 0.7900000214576721],
[1465980663.665972, 36, 0.7799999713897705],
[1465980663.697275, 37, 0.9100000262260437],
[1465980663.726395, 38, 0.8700000047683716],
[1465980663.760169, 39, 0.9200000166893005],
[1465980664.45205, 41, 0.8299999833106995],
[1465980664.484207, 42, 0.7599999904632568],
[1465980664.515375, 43, 0.7699999809265137],
[1465980664.547608, 44, 0.8299999833106995],
[1465980664.580122, 45, 0.949999988079071],
[1465980664.611019, 46, 0.8999999761581421],
[1465980664.642956, 47, 0.8700000047683716],
[1465980664.674636, 48, 0.8500000238418579],
[1465980664.705622, 49, 0.8899999856948853],
[1465980665.379549, 51, 0.8399999737739563],
[1465980665.422869, 52, 0.8500000238418579],
[1465980665.466136, 53, 0.8199999928474426],
[1465980665.508361, 54, 0.9300000071525574],
[1465980665.544331, 55, 0.9399999976158142],
[1465980665.589887, 56, 0.8700000047683716],
[1465980665.633466, 57, 0.9300000071525574],
[1465980665.674978, 58, 0.7799999713897705],
[1465980665.716878, 59, 0.9300000071525574],
[1465980666.653456, 61, 0.8799999952316284],
[1465980666.697294, 62, 0.9300000071525574],
[1465980666.742066, 63, 0.8700000047683716],
[1465980666.780127, 64, 0.8299999833106995],
[1465980666.818287, 65, 0.9200000166893005],
[1465980666.855386, 66, 0.9399999976158142],
[1465980666.897352, 67, 0.9300000071525574],
[1465980666.931322, 68, 0.8899999856948853],
[1465980666.96562, 69, 0.8600000143051147],
[1465980667.619625, 71, 0.8700000047683716],
[1465980667.655166, 72, 0.9200000166893005], [
1465980667.687101, 73, 0.8199999928474426
], [1465980667.720176, 74, 0.8100000023841858],
[1465980667.751985, 75, 0.8500000238418579], [
1465980667.785244, 76, 0.8600000143051147
], [1465980667.820445, 77, 0.9200000166893005], [
1465980667.857163, 78, 0.8899999856948853
], [1465980667.891868, 79, 0.8999999761581421], [
1465980668.56409, 81, 0.8500000238418579
], [1465980668.599529, 82, 0.8299999833106995], [
1465980668.630751, 83, 0.8500000238418579
], [1465980668.665135, 84, 0.8199999928474426], [
1465980668.697928, 85, 0.8199999928474426
], [1465980668.730525, 86, 0.8799999952316284], [
1465980668.769772, 87, 0.9200000166893005
], [1465980668.803344, 88, 0.8299999833106995], [
1465980668.834414, 89, 0.800000011920929
], [1465980669.814826, 91, 0.8600000143051147], [
1465980669.851511, 92, 0.8899999856948853
], [1465980669.891407, 93, 0.8799999952316284], [
1465980669.927507, 94, 0.9399999976158142
], [1465980669.968384, 95, 0.9300000071525574], [
1465980670.007071, 96, 0.8500000238418579
], [1465980670.044314, 97, 0.8500000238418579], [
1465980670.083472, 98, 0.9100000262260437
], [1465980670.214597, 99, 0.8600000143051147], [
1465980670.934513, 101, 0.8799999952316284
], [1465980670.971317, 102, 0.8700000047683716], [
1465980671.003626, 103, 0.8600000143051147
], [1465980671.037037, 104, 0.8399999737739563], [
1465980671.070037, 105, 0.9200000166893005
], [1465980671.104992, 106, 0.8600000143051147], [
1465980671.137882, 107, 0.8100000023841858
], [1465980671.173917, 108, 0.7400000095367432], [
1465980671.205898, 109, 0.8799999952316284
], [1465980671.833723, 111, 0.9100000262260437]]
def data():
return {
"test": {
"min/layer2/weights": {
"displayName": "min/layer2/weights",
"description": ""
}
},
"train": {
"min/layer2/weights": {
"displayName": "min/layer2/weights",
"description": ""
}
}
}
""" entry point of visual_dl
"""
import json
import os
import sys
from optparse import OptionParser
from flask import Flask, redirect
from flask import request
from flask import send_from_directory
from flask import Response
from visualdl.log import logger
import visualdl.mock.data as mock_data
import visualdl.mock.tags as mock_tags
app = Flask(__name__, static_url_path="")
def option_parser():
"""
:return:
"""
parser = OptionParser(usage="usage: visual_dl visual_dl.py "\
"-p port [options]")
parser.add_option(
"-p",
"--port",
type=int,
default=8040,
action="store",
dest="port",
help="rest api service port")
parser.add_option(
"--logdir", action="store", dest="logdir", help="log file directory")
return parser.parse_args()
options, args = option_parser()
server_path = os.path.abspath(os.path.dirname(sys.argv[0]))
static_file_path = "./frontend/dist/"
mock_data_path = "./mock_data/"
# return data
# status, msg, data
def gen_result(status, msg, data):
"""
:param status:
:param msg:
:return:
"""
result = dict()
result['status'] = status
result['msg'] = msg
result['data'] = data
return result
@app.route("/")
def index():
return redirect('/static/index.html', code=302)
@app.route('/static/<path:filename>')
def serve_static(filename):
return send_from_directory(
os.path.join(server_path, static_file_path), filename)
@app.route('/data/logdir')
def logdir():
result = gen_result(0, "", {"logdir": options.logdir})
return Response(json.dumps(result), mimetype='application/json')
@app.route('/data/runs')
def runs():
is_debug = bool(request.args.get('debug'))
result = gen_result(0, "", ["train", "test"])
return Response(json.dumps(result), mimetype='application/json')
@app.route("/data/plugin/scalars/tags")
def tags():
is_debug = bool(request.args.get('debug'))
result = gen_result(0, "", mock_tags.data())
return Response(json.dumps(result), mimetype='application/json')
@app.route('/data/plugin/scalars/scalars')
def scalars():
run = request.args.get('run')
tag = request.args.get('tag')
is_debug = bool(request.args.get('debug'))
result = gen_result(0, "", mock_data.sequence_data())
return Response(json.dumps(result), mimetype='application/json')
if __name__ == '__main__':
logger.info(" port=" + str(options.port))
app.run(debug=False, host="0.0.0.0", port=options.port)
#!/bin/bash
set -ex
sudo pip install numpy
#sudo apt-get install --only-upgrade cmake -y
mkdir -p build
cd build
cmake ..
make
make test
#if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./travis/run_on_pull_requests; fi
#if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./travis/run_on_non_pull_requests; fi
# This file is used by clang-format to autoformat paddle source code
#
# The clang-format is part of llvm toolchain.
# It need to install llvm and clang to format source code style.
#
# The basic usage is,
# clang-format -i -style=file PATH/TO/SOURCE/CODE
#
# The -style=file implicit use ".clang-format" file located in one of
# parent directory.
# The -i means inplace change.
#
# The document of clang-format is
# http://clang.llvm.org/docs/ClangFormat.html
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
---
Language: Cpp
BasedOnStyle: Google
IndentWidth: 2
TabWidth: 2
ContinuationIndentWidth: 4
AccessModifierOffset: -2 # The private/protected/public has no indent in class
Standard: Cpp11
AllowAllParametersOfDeclarationOnNextLine: true
BinPackParameters: false
BinPackArguments: false
...
add_library(sdk ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk.cc)
add_library(im ${PROJECT_SOURCE_DIR}/visualdl/logic/im.cc)
add_dependencies(im storage_proto)
add_dependencies(sdk storage_proto)
## pybind
add_library(core SHARED ${PROJECT_SOURCE_DIR}/visualdl/logic/pybind.cc)
add_dependencies(core pybind python im storage sdk protobuf glog)
target_link_libraries(core PRIVATE pybind python im storage sdk protobuf glog)
set_target_properties(core PROPERTIES PREFIX "" SUFFIX ".so")
#include <glog/logging.h>
#include <ctime>
#include "visualdl/logic/im.h"
namespace visualdl {
/*
* @num_samples: number of instances to sample
* @size: counter of the records.
* @returns: id of the instance to replace, if drop this instance, return -1.
*/
int ReserviorSample(int num_samples, int num_records) {
if (num_records <= num_samples) {
return num_records;
}
std::srand(std::time(0));
float prob = static_cast<float>(std::rand()) / RAND_MAX;
float receive_prob = static_cast<float>(num_samples) / num_records;
if (prob < receive_prob) {
int offset2replace = std::rand() % num_samples;
return offset2replace;
}
return -1;
}
void IM::SetPersistDest(const std::string &path) {
CHECK(storage_->mutable_data()->dir().empty())
<< "duplicate set storage's path";
storage_->mutable_data()->set_dir(path);
}
storage::Tablet *IM::AddTablet(const std::string &tag, int num_samples) {
auto tablet = storage_->NewTablet(tag, num_samples);
return tablet;
}
void IM::AddRecord(const std::string &tag, const storage::Record &data) {
auto *tablet = storage_->tablet(tag);
CHECK(tablet) << "no tablet called " << tag;
auto num_records = tablet->total_records();
const auto num_samples = tablet->num_samples();
int offset;
// use reservoir sampling or not
if (num_samples > 0) {
offset = ReserviorSample(num_samples, num_records + 1);
if (offset < 0) return;
} else {
offset = num_records;
}
storage::Record *record;
if (offset >= num_records) {
record = tablet->add_records();
} else {
record = tablet->mutable_records(offset);
}
*record = data;
tablet->set_total_records(num_records + 1);
}
void IM::Clear() {
auto *data = storage().mutable_data();
data->clear_tablets();
data->clear_dir();
data->clear_timestamp();
}
void IM::PersistToDisk() {
CHECK(!storage_->data().dir().empty()) << "path of storage should be set";
// TODO make dir first
// MakeDir(storage_.data().dir());
storage_->PersistToDisk(storage_->data().dir());
}
} // namespace visualdl
#ifndef VISUALDL_LOGIC_IM_H
#define VISUALDL_LOGIC_IM_H
#include <glog/logging.h>
#include <memory>
#include <mutex>
#include <string>
#include "visualdl/storage/storage.h"
#include "visualdl/utils/concurrency.h"
namespace visualdl {
/*
* IM(Information Maintainer) maintain the Storage singleton in memory,
* pre-compute some the statistical information to help visualizaton.
*
* There should be two processes and each have an IM, one is the web server
* which hold one IM to read the storage, the other is the SDK(python or C++),
* it will get an IM to write latest changes to storage.
*
* An IM have an underlying Storage object, which might be a memory based
* storage or a disk based one, both has the same interfaces those defined by
* class StorageBase.
*
* The SDK's IM will maintain the changes and periodically write to disk, and
* the web server's IM will periodically read latest storage from disk.
*/
class IM final {
public:
IM() { storage_.reset(new MemoryStorage(&executor_)); }
// IM(StorageBase::Type type, StorageBase::Mode mode);
~IM() { executor_.Quit(); }
void MaintainRead(const std::string &dir, int msecs) {
LOG(INFO) << "start maintain read";
dynamic_cast<MemoryStorage *>(storage_.get())
->StartReadService(dir, msecs, &lock_);
}
void MaintainWrite(const std::string &dir, int msecs) {
dynamic_cast<MemoryStorage *>(storage_.get())
->StartWriteService(dir, msecs, &lock_);
}
/*
* Set the disk path to store the Storage object.
*/
void SetPersistDest(const std::string &path);
storage::Tablet *AddTablet(const std::string &tag, int num_samples);
/*
* @tag: tag of the target Tablet.
* @record: a record
*
* NOTE pass in the serialized protobuf message will trigger copying, but
* simpler to support different Tablet data formats.
*/
void AddRecord(const std::string &tag, const storage::Record &record);
/*
* delete all the information.
*/
void Clear();
/*
* Save the Storage Protobuf to disk.
*/
void PersistToDisk();
StorageBase &storage() { return *storage_; }
cc::PeriodExector &executor() { return executor_; }
std::mutex &handler() { return lock_; }
private:
// read write lock for protobuf in memory
// TODO(ChunweiYan) mutex too heavy here, might change to a message queue to
// reduce the frequency of visiting disk
std::mutex lock_;
std::unique_ptr<StorageBase> storage_;
cc::PeriodExector executor_;
};
} // namespace visualdl
#endif // VISUALDL_BACKEND_LOGIC_IM_H
#include "visualdl/logic/im.h"
#include "gtest/gtest.h"
#include "visualdl/storage/storage.h"
namespace visualdl {
class ImTester : public ::testing::Test {
protected:
void SetUp() override {}
IM im;
};
TEST_F(ImTester, AddTablet) {
im.Clear();
im.AddTablet("tag0", 20);
}
TEST_F(ImTester, AddRecord) {
im.Clear();
im.AddTablet("tag0", 20);
for (int i = 0; i < 100; i++) {
storage::Record rcd;
rcd.set_dtype(storage::DataType::kInt32s);
for (int j = 0; j < 10; j++) {
rcd.add_data()->add_i32s(i * 20 + j);
}
im.AddRecord("tag0", rcd);
}
ASSERT_EQ(im.storage().tablet("tag0")->records_size(), 100UL);
}
} // namespace visualdl
#include <ctype.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "visualdl/logic/sdk.h"
namespace py = pybind11;
namespace vs = visualdl;
PYBIND11_PLUGIN(core) {
py::module m("core", "C++ core of VisualDL");
// m.doc() = "visualdl python core API";
py::class_<vs::TabletHelper>(m, "Tablet")
// other member setter and getter
.def("record_buffer", &vs::TabletHelper::record_buffer)
.def("records_size", &vs::TabletHelper::records_size)
.def("buffer", &vs::TabletHelper::buffer)
.def("human_readable_buffer", &vs::TabletHelper::human_readable_buffer)
.def("set_buffer",
(void (vs::TabletHelper::*)(const std::string&)) &
vs::TabletHelper::SetBuffer)
// scalar interface
.def("as_int32_scalar",
[](vs::TabletHelper& self, vs::ImHelper& im) {
return vs::components::ScalarHelper<int32_t>(self, &im.handler());
})
.def("as_int64_scalar",
[](vs::TabletHelper& self, vs::ImHelper& im) {
return vs::components::ScalarHelper<int64_t>(&self.data(),
&im.handler());
})
.def("as_float_scalar",
[](vs::TabletHelper& self, vs::ImHelper& im) {
return vs::components::ScalarHelper<float>(&self.data(),
&im.handler());
})
.def("as_double_scalar", [](vs::TabletHelper& self, vs::ImHelper& im) {
return vs::components::ScalarHelper<double>(&self.data(),
&im.handler());
});
py::class_<vs::StorageHelper>(m, "Storage")
.def("timestamp", &vs::StorageHelper::timestamp)
.def("dir", &vs::StorageHelper::dir)
.def("set_dir", &vs::StorageHelper::SetDir)
.def("tablets_size", &vs::StorageHelper::tablets_size)
.def("buffer", &vs::StorageHelper::buffer)
.def("human_readable_buffer", &vs::StorageHelper::human_readable_buffer)
.def("set_buffer",
(void (vs::StorageHelper::*)(const std::string&)) &
vs::StorageHelper::SetBuffer);
py::class_<vs::ImHelper>(m, "Im")
.def("__init__",
[](vs::ImHelper& instance) { new (&instance) vs::ImHelper(); })
.def("storage", &vs::ImHelper::storage)
.def("tablet", &vs::ImHelper::tablet)
.def("add_tablet", &vs::ImHelper::AddTablet)
.def("persist_to_disk", &vs::ImHelper::PersistToDisk)
.def("clear_tablets", &vs::ImHelper::ClearTablets)
.def("start_read_service",
&vs::ImHelper::StartReadService,
"start a thread to maintain read service")
.def("start_write_service",
&vs::ImHelper::StartWriteSerice,
"start a thread to maintain write service")
.def("stop_service",
&vs::ImHelper::StopService,
"stop the service thread");
// interfaces for components begin
// different data type of scalar conponent
#define ADD_SCALAR_TYPED_INTERFACE(T, name__) \
py::class_<vs::components::ScalarHelper<T>>(m, #name__) \
.def("add_record", &vs::components::ScalarHelper<T>::AddRecord) \
.def("set_captions", &vs::components::ScalarHelper<T>::SetCaptions) \
.def("get_records", &vs::components::ScalarHelper<T>::GetRecords) \
.def("get_captions", &vs::components::ScalarHelper<T>::GetCaptions) \
.def("get_ids", &vs::components::ScalarHelper<T>::GetIds) \
.def("get_record_size", &vs::components::ScalarHelper<T>::GetSize) \
.def("get_timestamps", &vs::components::ScalarHelper<T>::GetTimestamps);
ADD_SCALAR_TYPED_INTERFACE(int32_t, ScalarInt32);
ADD_SCALAR_TYPED_INTERFACE(int64_t, ScalarInt64);
ADD_SCALAR_TYPED_INTERFACE(float, ScalarFloat);
ADD_SCALAR_TYPED_INTERFACE(double, ScalarDouble);
#undef ADD_SCALAR_TYPED_INTERFACE
}
#include "visualdl/logic/sdk.h"
#include <google/protobuf/text_format.h>
namespace visualdl {
#define IMPL_ENTRY_SET_OR_ADD(method__, ctype__, dtype__, opr__) \
template <> \
void EntryHelper<ctype__>::method__(ctype__ v) { \
entry->set_dtype(storage::DataType::dtype__); \
entry->opr__(v); \
}
IMPL_ENTRY_SET_OR_ADD(Set, int32_t, kInt32, set_i32);
IMPL_ENTRY_SET_OR_ADD(Set, int64_t, kInt64, set_i64);
IMPL_ENTRY_SET_OR_ADD(Set, bool, kBool, set_b);
IMPL_ENTRY_SET_OR_ADD(Set, float, kFloat, set_f);
IMPL_ENTRY_SET_OR_ADD(Set, double, kDouble, set_d);
IMPL_ENTRY_SET_OR_ADD(Add, int32_t, kInt32s, add_i32s);
IMPL_ENTRY_SET_OR_ADD(Add, int64_t, kInt64s, add_i64s);
IMPL_ENTRY_SET_OR_ADD(Add, float, kFloats, add_fs);
IMPL_ENTRY_SET_OR_ADD(Add, double, kDoubles, add_ds);
IMPL_ENTRY_SET_OR_ADD(Add, std::string, kStrings, add_ss);
IMPL_ENTRY_SET_OR_ADD(Add, bool, kBools, add_bs);
#define IMPL_ENTRY_GET(T, fieldname__) \
template <> \
T EntryHelper<T>::Get() const { \
return entry->fieldname__(); \
}
IMPL_ENTRY_GET(int32_t, i32);
IMPL_ENTRY_GET(int64_t, i64);
IMPL_ENTRY_GET(float, f);
IMPL_ENTRY_GET(double, d);
IMPL_ENTRY_GET(std::string, s);
IMPL_ENTRY_GET(bool, b);
#define IMPL_ENTRY_GET_MULTI(T, fieldname__) \
template <> \
std::vector<T> EntryHelper<T>::GetMulti() const { \
return std::vector<T>(entry->fieldname__().begin(), \
entry->fieldname__().end()); \
}
IMPL_ENTRY_GET_MULTI(int32_t, i32s);
IMPL_ENTRY_GET_MULTI(int64_t, i64s);
IMPL_ENTRY_GET_MULTI(float, fs);
IMPL_ENTRY_GET_MULTI(double, ds);
IMPL_ENTRY_GET_MULTI(std::string, ss);
IMPL_ENTRY_GET_MULTI(bool, bs);
std::string StorageHelper::human_readable_buffer() const {
std::string buffer;
google::protobuf::TextFormat::PrintToString(*data_, &buffer);
return buffer;
}
std::string TabletHelper::human_readable_buffer() const {
std::string buffer;
google::protobuf::TextFormat::PrintToString(*data_, &buffer);
return buffer;
}
// implementations for components
namespace components {
template <typename T>
void ScalarHelper<T>::SetCaptions(const std::vector<std::string> &captions) {
ACQUIRE_HANDLER(handler_);
CHECK_EQ(data_->captions_size(), 0UL) << "the captions can set only once";
for (int i = 0; i < captions.size(); i++) {
data_->add_captions(captions[i]);
}
}
template <typename T>
void ScalarHelper<T>::AddRecord(int id, const std::vector<T> &values) {
ACQUIRE_HANDLER(handler_);
CHECK_NOTNULL(data_);
CHECK_GT(data_->captions_size(), 0UL) << "captions should be set first";
CHECK_EQ(data_->captions_size(), values.size())
<< "number of values in a record should be compatible with the "
"captions";
// add record data
auto *record = data_->add_records();
auto *data = record->add_data();
EntryHelper<T> entry_helper(data);
for (auto v : values) {
entry_helper.Add(v);
}
// set record id
record->set_id(id);
// set record timestamp
record->set_timestamp(time(NULL));
}
template <typename T>
std::vector<std::vector<T>> ScalarHelper<T>::GetRecords() const {
ACQUIRE_HANDLER(handler_);
std::vector<std::vector<T>> result;
EntryHelper<T> entry_helper;
for (int i = 0; i < data_->records_size(); i++) {
auto *entry = data_->mutable_records(i)->mutable_data(0);
entry_helper(entry);
auto datas = entry_helper.GetMulti();
result.push_back(std::move(datas));
}
return result;
}
template <typename T>
std::vector<int> ScalarHelper<T>::GetIds() const {
ACQUIRE_HANDLER(handler_);
CHECK_NOTNULL(data_);
std::vector<int> result;
for (int i = 0; i < data_->records_size(); i++) {
result.push_back(data_->records(i).id());
}
return result;
}
template <typename T>
std::vector<int> ScalarHelper<T>::GetTimestamps() const {
ACQUIRE_HANDLER(handler_);
CHECK_NOTNULL(data_);
std::vector<int> result;
for (int i = 0; i < data_->records_size(); i++) {
result.push_back(data_->records(i).timestamp());
}
return result;
}
template <typename T>
std::vector<std::string> ScalarHelper<T>::GetCaptions() const {
ACQUIRE_HANDLER(handler_);
return std::vector<std::string>(data_->captions().begin(),
data_->captions().end());
}
template class ScalarHelper<int32_t>;
template class ScalarHelper<int64_t>;
template class ScalarHelper<float>;
template class ScalarHelper<double>;
} // namespace components
} // namespace visualdl
#ifndef VISUALDL_LOGIC_SDK_H
#define VISUALDL_LOGIC_SDK_H
#include <glog/logging.h>
#include <time.h>
#include <map>
#include "visualdl/logic/im.h"
namespace visualdl {
/*
* Utility helper for storage::Entry.
*/
template <typename T>
struct EntryHelper {
// use pointer to avoid copy
storage::Entry *entry{nullptr};
EntryHelper() {}
explicit EntryHelper(storage::Entry *entry) : entry(entry) {}
void operator()(storage::Entry *entry) { this->entry = entry; }
/*
* Set a single value.
*/
void Set(T v);
/*
* Add a value to repeated message field.
*/
void Add(T v);
/*
* Get a single value.
*/
T Get() const;
/*
* Get repeated field.
*/
std::vector<T> GetMulti() const;
};
class TabletHelper {
public:
// basic member getter and setter
std::string record_buffer(int idx) const {
return data_->records(idx).SerializeAsString();
}
size_t records_size() const { return data_->records_size(); }
std::string buffer() const { return data_->SerializeAsString(); }
std::string human_readable_buffer() const;
void SetBuffer(const storage::Tablet &t) { *data_ = t; }
void SetBuffer(const std::string &b) { data_->ParseFromString(b); }
storage::Tablet &data() const { return *data_; }
// constructor that enable concurrency.
TabletHelper(storage::Tablet *t) : data_(t) {}
// data updater that resuage of one instance.
TabletHelper &operator()(storage::Tablet *t) {
data_ = t;
return *this;
}
private:
storage::Tablet *data_;
};
class StorageHelper {
public:
StorageHelper(storage::Storage *s) : data_(s) {}
StorageHelper &operator()(storage::Storage *s) {
data_ = s;
return *this;
}
void SetBuffer(const storage::Storage &buffer) { *data_ = buffer; }
void SetBuffer(const std::string &buffer) { data_->ParseFromString(buffer); }
void SetDir(const std::string &dir) {
CHECK(data_) << "no storage instance hold";
data_->set_dir(dir);
}
int64_t timestamp() const { return data_->timestamp(); }
std::string dir() const { return data_->dir(); }
int tablets_size() const { return data_->tablets_size(); }
std::string buffer() const { return data_->SerializeAsString(); }
std::string human_readable_buffer() const;
private:
storage::Storage *data_{nullptr};
};
class ImHelper {
public:
// TODO(ChunweiYan) decouple helper with resource.
ImHelper() { im_.reset(new IM); }
ImHelper(std::unique_ptr<IM> im) : im_(std::move(im)) {}
StorageHelper storage() {
return StorageHelper(im_->storage().mutable_data());
}
TabletHelper tablet(const std::string &tag) {
return TabletHelper(im_->storage().tablet(tag));
}
TabletHelper AddTablet(const std::string &tag, int num_samples) {
return TabletHelper(im_->AddTablet(tag, num_samples));
}
std::mutex &handler() { return im_->handler(); }
void ClearTablets() { im_->storage().mutable_data()->clear_tablets(); }
void StartReadService(const std::string &dir, int msecs) {
im_->SetPersistDest(dir);
im_->MaintainRead(dir, msecs);
}
void StartWriteSerice(const std::string &dir, int msecs) {
im_->SetPersistDest(dir);
im_->MaintainWrite(dir, msecs);
}
void StopService() { im_->executor().Quit(); }
void PersistToDisk() const { im_->PersistToDisk(); }
private:
std::unique_ptr<IM> im_;
};
namespace components {
#define ACQUIRE_HANDLER(handler) std::lock_guard<std::mutex> ____(*handler);
/*
* Read and write support for Scalar component.
*/
template <typename T>
class ScalarHelper {
public:
ScalarHelper(storage::Tablet *tablet, std::mutex *handler = nullptr)
: data_(tablet), handler_(handler) {}
ScalarHelper(TabletHelper &tablet, std::mutex *handler = nullptr)
: data_(&tablet.data()), handler_(handler) {}
void SetCaptions(const std::vector<std::string> &captions);
void AddRecord(int id, const std::vector<T> &values);
std::vector<std::vector<T>> GetRecords() const;
std::vector<int> GetIds() const;
std::vector<int> GetTimestamps() const;
std::vector<std::string> GetCaptions() const;
size_t GetSize() const { return data_->records_size(); }
private:
storage::Tablet *data_;
std::mutex *handler_;
};
} // namespace components
} // namespace visualdl
#endif // VISUALDL_BACKEND_LOGIC_SDK_H
#include "visualdl/logic/sdk.h"
#include <gtest/gtest.h>
namespace visualdl {
struct ScalarTestHelper {
ImHelper rim;
ImHelper wim;
const std::string dir = "./tmp/sdk_test.test";
void operator()(std::function<void()> read, std::function<void()> write) {
wim.StartWriteSerice(dir, 200);
write();
// should wait for the write service create log's path
std::this_thread::sleep_for(std::chrono::milliseconds(400));
rim.StartReadService(dir, 100);
// should wait for the read service to load "tag0" tablet into memory
std::this_thread::sleep_for(std::chrono::milliseconds(600));
read();
}
};
TEST(Scalar, set_caption) {
ScalarTestHelper helper;
const std::vector<std::string> captions({"train", "test"});
auto write = [&] {
auto tablet = helper.wim.AddTablet("tag0", -1);
components::ScalarHelper<float> scalar(tablet, &helper.wim.handler());
scalar.SetCaptions(captions);
};
auto read = [&] {
auto mytablet = helper.rim.tablet("tag0");
components::ScalarHelper<float> myscalar(mytablet, &helper.rim.handler());
auto mycaptions = myscalar.GetCaptions();
ASSERT_EQ(captions, mycaptions);
};
helper(read, write);
}
TEST(Scalar, add_records) {
ScalarTestHelper helper;
const std::vector<std::string> captions({"train", "test"});
const size_t nsteps = 100;
auto write = [&] {
auto tablet = helper.wim.AddTablet("tag0", -1);
components::ScalarHelper<float> scalar(tablet, &helper.wim.handler());
scalar.SetCaptions(captions);
for (int i = 0; i < nsteps; i++) {
scalar.AddRecord(i * 10, std::vector<float>({(float)i, (float)i + 1}));
}
};
auto read = [&] {
auto mytablet = helper.rim.tablet("tag0");
components::ScalarHelper<float> myscalar(mytablet, &helper.rim.handler());
auto records = myscalar.GetRecords();
ASSERT_EQ(records.size(), nsteps);
for (int i = 0; i < nsteps; i++) {
ASSERT_EQ(records[i], std::vector<float>({(float)i, (float)i + 1}));
}
};
helper(read, write);
}
} // namespace visualdl
function(py_test TARGET_NAME)
set(options STATIC static SHARED shared)
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS ARGS)
cmake_parse_arguments(py_test "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
add_test(NAME ${TARGET_NAME}
COMMAND env PYTHONPATH=${CMAKE_BINARY_DIR}/visualdl/logic
${PYTHON_EXECUTABLE} -u ${py_test_SRCS} ${py_test_ARGS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endfunction()
py_test(test_summary SRCS test_summary.py)
__all__ = [
'set_storage',
'scalar',
]
import core
dtypes = ("float", "double", "int32", "int64")
def IM(dir, mode="read", msecs=500):
im = core.Im()
READ = "read"
WRITE = "write"
if mode == READ:
im.start_read_service(dir, msecs)
else:
im.start_write_service(dir, msecs)
return im
class _Scalar(object):
'''
Python syntax wrapper for the core.ScalarHelper object.
'''
def __init__(self, core_object):
self._core_object = core_object
def add(self, id, vs):
'''
add a scalar record
:param id: int
id in the x-corrdinate
:param vs: list
values
:return: None
'''
self._core_object.add_record(id, vs)
def set_captions(self, cs):
'''
set the captions, one caption for one line.
:param cs: list of str
:return: None
'''
self._core_object.set_captions(cs)
@property
def captions(self):
return self._core_object.get_captions()
@property
def records(self):
'''
get all the records, format like
[
[0.1, 0.2], # first record
[0.2, 0.3], # second record
# ...
]
:return: list of list
'''
return self._core_object.get_records()
@property
def ids(self):
'''
get all the ids for the records
:return: list of int
'''
return self._core_object.get_ids()
@property
def timestamps(self):
'''
get all the timestamps for the records
:return: list of int
'''
return self._core_object.get_timestamps()
@property
def size(self):
return self._core_object.get_record_size()
def scalar(im, tag, dtype='float'):
'''
create a scalar component.
:param tag: str
name of this component.
:param dtype: string
the data type that will be used in underlying storage.
:return: object of core.Tablet
'''
assert dtype in dtypes, "invalid dtype(%s), should be one of %s" % (
dtype, str(dtypes))
tablet = im.add_tablet(tag, -1)
dtype2obj = {
'float': tablet.as_float_scalar,
'double': tablet.as_double_scalar,
'int32': tablet.as_int32_scalar,
'int64': tablet.as_int64_scalar,
}
obj = dtype2obj[dtype](im)
return _Scalar(obj)
import summary
import numpy as np
import unittest
import time
class StorageTester(unittest.TestCase):
def test_storage(self):
summary.set_writable_storage("./tmp_dir")
time.sleep(5)
summary.stop_service()
if __name__ == '__main__':
unittest.main()
import summary
import numpy as np
import unittest
import random
import time
once_flag = False
class ScalarTester(unittest.TestCase):
def setUp(self):
self.dir = "tmp/summary.test"
# clean path
try:
os.rmdir(self.dir)
except:
pass
self.im = summary.IM(self.dir, "write", 200)
self.tablet_name = "scalar0"
self.scalar = summary.scalar(self.im, self.tablet_name)
self.py_captions = ["train cost", "test cost"]
self.scalar.set_captions(self.py_captions)
self.py_records = []
self.py_ids = []
# write
for i in range(10):
record = [0.1 * i, 0.2 * i]
id = i * 10
self.py_records.append(record)
self.py_ids.append(id)
self.scalar.add(id, record)
def test_records(self):
self.assertEqual(self.scalar.size, len(self.py_records))
for i, record in enumerate(self.scalar.records):
self.assertTrue(np.isclose(record, self.py_records[i]).all())
def test_ids(self):
self.assertEqual(len(self.py_ids), self.scalar.size)
for i, id in enumerate(self.scalar.ids):
self.assertEqual(self.py_ids[i], id)
def test_captions(self):
self.assertEqual(self.scalar.captions, self.py_captions)
def test_read_records(self):
time.sleep(1)
im = summary.IM(self.dir, "read", 200)
time.sleep(1)
scalar = summary.scalar(im, self.tablet_name)
records = scalar.records
self.assertEqual(len(self.py_records), scalar.size)
for i, record in enumerate(self.scalar.records):
self.assertTrue(np.isclose(record, records[i]).all())
def test_read_ids(self):
time.sleep(0.6)
im = summary.IM(self.dir, "read", msecs=200)
time.sleep(0.6)
scalar = summary.scalar(im, self.tablet_name)
self.assertEqual(len(self.py_ids), scalar.size)
for i, id in enumerate(scalar.ids):
self.assertEqual(self.py_ids[i], id)
def test_read_captions(self):
time.sleep(0.6)
im = summary.IM(self.dir, "read", msecs=200)
time.sleep(0.6)
scalar = summary.scalar(im, self.tablet_name)
self.assertEqual(scalar.captions, self.py_captions)
def test_mix_read_write(self):
write_im = summary.IM(self.dir, "write", msecs=200)
time.sleep(0.6)
read_im = summary.IM(self.dir, "read", msecs=200)
scalar_writer = summary.scalar(write_im, self.tablet_name)
scalar_reader = summary.scalar(read_im, self.tablet_name)
scalar_writer.set_captions(["train cost", "test cost"])
for i in range(1000):
scalar_writer.add(i, [random.random(), random.random()])
scalar_reader.records
for i in range(500):
scalar_writer.add(i, [random.random(), random.random()])
scalar_reader.records
for i in range(500):
scalar_writer.add(i, [random.random(), random.random()])
for i in range(10):
scalar_reader.records
scalar_reader.captions
if __name__ == '__main__':
unittest.main()
import summary
import numpy as np
import unittest
import time
class StorageTester(unittest.TestCase):
def test_read_storage(self):
summary.set_readable_storage("./tmp")
time.sleep(1)
scalar = summary.read_scalar('tag01')
time.sleep(5)
summary.stop_service()
if __name__ == '__main__':
unittest.main()
## add storage_proto as target
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS storage.proto)
add_library(storage_proto ${PROTO_SRCS})
add_dependencies(storage_proto protobuf)
## add storage as target
add_library(storage storage.cc storage.h ${PROTO_SRCS} ${PROTO_HDRS})
add_dependencies(storage storage_proto)
#include <glog/logging.h>
#include <fstream>
#include "visualdl/storage/storage.h"
#include "visualdl/utils/concurrency.h"
#include "visualdl/utils/filesystem.h"
namespace visualdl {
const std::string StorageBase::meta_file_name = "storage.meta";
std::string StorageBase::meta_path(const std::string &dir) const {
CHECK(!dir.empty()) << "dir is empty";
return dir + "/" + meta_file_name;
}
std::string StorageBase::tablet_path(const std::string &dir,
const std::string &tag) const {
CHECK(!dir.empty()) << "dir should be set first";
return dir + "/" + tag;
}
storage::Tablet *MemoryStorage::NewTablet(const std::string &tag,
int num_samples) {
auto it = tablets_.find(tag);
if (it == tablets_.end()) {
// create new tablet
tablets_[tag] = storage::Tablet();
tablets_[tag].set_tag(tag);
*storage_.add_tags() = tag;
} else {
return &it->second;
}
return &tablets_[tag];
}
storage::Tablet *MemoryStorage::tablet(const std::string &tag) {
auto it = tablets_.find(tag);
CHECK(it != tablets_.end()) << "tablet tagged as " << tag << " not exists";
return &it->second;
}
// TODO add some checksum to avoid unnecessary saving
void MemoryStorage::PersistToDisk(const std::string &dir) {
CHECK(!dir.empty());
storage_.set_dir(dir);
// make a directory if not exist
fs::TryRecurMkdir(dir);
// write storage out
VLOG(2) << "to serize meta to dir " << dir;
fs::SerializeToFile(storage_, meta_path(dir));
VLOG(2) << "serize meta to dir " << dir;
// write all the tablets
for (auto tag : storage_.tags()) {
auto it = tablets_.find(tag);
CHECK(it != tablets_.end());
fs::SerializeToFile(it->second, tablet_path(dir, tag));
}
}
// TODO add some checksum to avoid unnecessary loading
void MemoryStorage::LoadFromDisk(const std::string &dir) {
CHECK(!dir.empty()) << "dir is empty";
storage_.set_dir(dir);
// load storage
CHECK(fs::DeSerializeFromFile(&storage_, meta_path(dir)))
<< "parse from " << meta_path(dir) << " failed";
// load all the tablets
for (int i = 0; i < storage_.tags_size(); i++) {
auto tag = storage_.tags(i);
CHECK(fs::DeSerializeFromFile(&tablets_[tag], tablet_path(dir, tag)));
}
}
void MemoryStorage::StartReadService(const std::string &dir,
int msecs,
std::mutex *handler) {
CHECK(executor_ != nullptr);
CHECK(!dir.empty()) << "dir should be set first";
cc::PeriodExector::task_t task = [dir, this, handler] {
VLOG(1) << "loading from " << dir;
if (handler != nullptr) {
std::lock_guard<std::mutex> _(*handler);
LoadFromDisk(dir);
} else {
LoadFromDisk(dir);
}
return true;
};
// executor_.Start();
VLOG(1) << "push read task";
(*executor_)(std::move(task), msecs);
}
void MemoryStorage::StartWriteService(const std::string &dir,
int msecs,
std::mutex *handler) {
CHECK(executor_ != nullptr);
CHECK(!dir.empty()) << "dir should be set first";
storage_.set_dir(dir);
// executor_.Start();
cc::PeriodExector::task_t task = [dir, handler, this] {
VLOG(2) << "persist to disk";
if (handler != nullptr) {
std::lock_guard<std::mutex> _(*handler);
PersistToDisk(dir);
} else {
PersistToDisk(dir);
}
return true;
};
(*executor_)(std::move(task), msecs);
}
} // namespace visualdl
#ifndef VISUALDL_STORAGE_H
#define VISUALDL_STORAGE_H
#include <time.h>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include "visualdl/storage/storage.pb.h"
#include "visualdl/utils/concurrency.h"
namespace visualdl {
/*
* Generate a tablet path in disk from its tag.
*/
inline std::string GenPathFromTag(const std::string &dir,
const std::string &tag);
/*
* Storage Interface. The might be a bunch of implementations, for example, a
* MemStorage that keep a copy of all the taplets in memory, can be changed with
* a higher performance; a DiskStorage that keep all the data in disk, apply to
* the scenerios where memory consumption should be considered.
*/
class StorageBase {
public:
const static std::string meta_file_name;
enum Type { kMemory = 0, kDisk = 1 };
// mode of the sevice, either reading or writing.
enum Mode { kRead = 0, kWrite = 1, kNone = 2 };
void SetStorage(const std::string &dir) {
time_t t;
time(&t);
storage_.set_timestamp(t);
storage_.set_dir(dir);
}
std::string meta_path(const std::string &dir) const;
std::string tablet_path(const std::string &dir, const std::string &tag) const;
/*
* Create a new Tablet storage.
*/
virtual storage::Tablet *NewTablet(const std::string &tag,
int num_samples) = 0;
/*
* Get a tablet from memory, this can be viewed as a cache, if the storage is
* in disk, a hash map in memory will first load the corresponding Tablet
* Protobuf from disk and hold all the changes.
*/
virtual storage::Tablet *tablet(const std::string &tag) = 0;
/*
* Persist the data from cache to disk. Both the memory storage or disk
* storage should write changes to disk for persistence.
*/
virtual void PersistToDisk(const std::string &dir) = 0;
/*
* Load data from disk.
*/
virtual void LoadFromDisk(const std::string &dir) = 0;
storage::Storage *mutable_data() { return &storage_; }
const storage::Storage &data() { return storage_; }
protected:
storage::Storage storage_;
};
/*
* Storage in Memory, that will support quick edits on data.
*/
class MemoryStorage final : public StorageBase {
public:
MemoryStorage() {}
MemoryStorage(cc::PeriodExector *executor) : executor_(executor) {}
~MemoryStorage() {
if (executor_ != nullptr) executor_->Quit();
}
storage::Tablet *NewTablet(const std::string &tag, int num_samples) override;
storage::Tablet *tablet(const std::string &tag) override;
void PersistToDisk(const std::string &dir) override;
void LoadFromDisk(const std::string &dir) override;
/*
* Create a thread which will keep reading the latest data from the disk to
* memory.
*
* msecs: how many millisecond to sync memory and disk.
*/
void StartReadService(const std::string &dir, int msecs, std::mutex *handler);
/*
* Create a thread which will keep writing the latest changes from memory to
* disk.
*
* msecs: how many millisecond to sync memory and disk.
*/
void StartWriteService(const std::string &dir,
int msecs,
std::mutex *handler);
private:
std::map<std::string, storage::Tablet> tablets_;
// TODO(ChunweiYan) remove executor here.
cc::PeriodExector *executor_{nullptr};
};
} // namespace visualdl
#endif // VISUALDL_STORAGE_H
syntax = "proto3";
package storage;
enum DataType {
// single entry
kInt32 = 0;
kInt64 = 1;
kFloat = 2;
kDouble = 3;
kString = 4;
kBool = 5;
// entrys
kInt64s = 6;
kFloats = 7;
kDoubles = 8;
kStrings = 9;
kInt32s = 10;
kBools = 11;
kUnknown = 12;
}
// A data array, which type is `type`.
message Entry {
// if all the entries in a record share the same data type, ignore this field
// and store type to `dtype` in `Record`.
DataType dtype = 1;
// single element
int32 i32 = 2;
int64 i64 = 3;
string s = 4;
float f = 5;
double d = 6;
bool b = 7;
// array
repeated int64 i64s = 8;
repeated float fs = 9;
repeated double ds = 10;
repeated int32 i32s = 11;
repeated string ss = 12;
repeated bool bs = 13;
}
/*
The Record proto is designed to represent any data structure for any component,
for
example, to store a record of Scalar component
Record {
// training error is 0.1, testing error is 0.2
data = [0.1, 0.2],
timestamp = xxxx,
// step id
id = xxxx
}
to store a record of Image component
Record {
// RBG pixel weights of a image
data = [[0.1, 0.2, ...], [...], [...]],
timestamp = xxxx,
// image shape
meta = [100, 200]
}
for other complex structure which have more fields than `timestamp`, `id` and
meta, the additional fields
can store in the `data` field, for it can store a list of values in different
basic data types.
A component handlers in logic layer should know how to write or load a record
for the corresponding component.
*/
message Record {
// one record might have multiple fields, one specific component should know
// how
// to parse the records.
repeated Entry data = 1;
// NOTE the timestamp, id, dtype might be useless for that all the meta info
// can
// be stored in `data` field.
int64 timestamp = 2;
int64 id = 3;
DataType dtype = 4;
// shape or some other meta infomation for this record, if all the records
// share the same meta, just store one copy of meta in `Storage`, or create
// a unique copy for each `Record`.
Entry meta = 5;
}
/*
A Tablet stores the records of a component which type is `component` and
indidates as `tag`.
The records will be saved in a file which name contains `tag`. During the
running period,
`num_records` will be accumulated, and `num_samples` indicates the size of
sample set the
reservoir sampling algorithm will collect.
*/
message Tablet {
// the kinds of the components that supported
enum Type {
kScalar = 0;
kHistogram = 1;
kGraph = 2;
}
// type of the component, different component should have different storage
// format.
Type component = 1;
// records the total count of records, each Write operation should increate
// this value.
int64 total_records = 2;
// indicate the number of instances to sample, this should be a constant
// value.
int32 num_samples = 3;
repeated Record records = 4;
// store a meta infomation if all the records share.
Entry meta = 5;
// the unique identification for this `Tablet`.
string tag = 6;
// one tablet might have multiple captions, for example, a scalar component
// might have
// two plots labeled "train" and "test".
repeated string captions = 7;
}
/*
The Storage stores all the records.
*/
message Storage {
// tags to Tablet, should be thread safe if fix the keys after initialization.
// TODO to delete in the new storage interface.
map<string, Tablet> tablets = 1;
repeated string tags = 4;
string dir = 2;
int64 timestamp = 3;
}
#include "visualdl/storage/storage.h"
#include <glog/logging.h>
#include <gtest/gtest.h>
namespace visualdl {
using namespace std;
class MemoryStorageTest : public ::testing::Test {
public:
void SetUp() override { storage_.SetStorage("./tmp"); }
MemoryStorage storage_;
};
TEST_F(MemoryStorageTest, SetStorage) {
string dir = "./tmp";
storage_.SetStorage(dir);
ASSERT_EQ(storage_.data().dir(), dir);
}
TEST_F(MemoryStorageTest, AddTablet) {
// TODO need to escape tag as name
string tag = "add%20tag0";
storage_.NewTablet(tag, -1);
auto* tablet = storage_.tablet(tag);
ASSERT_TRUE(tablet != nullptr);
ASSERT_EQ(tablet->tag(), tag);
}
TEST_F(MemoryStorageTest, PersistToDisk) {
const std::string dir = "./tmp/201.test";
storage_.SetStorage(dir);
string tag = "add%20tag0";
storage_.NewTablet(tag, -1);
storage_.PersistToDisk(dir);
LOG(INFO) << "persist to disk";
MemoryStorage other;
other.LoadFromDisk(dir);
LOG(INFO) << "read from disk";
ASSERT_EQ(other.data().SerializeAsString(),
storage_.data().SerializeAsString());
}
} // namespace visualdl
#include "gtest/gtest.h"
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
\ No newline at end of file
import sys
import unittest
import numpy as np
sys.path.append('../../build')
import core
im = core.im()
class StorageTester(unittest.TestCase):
def setUp(self):
im.clear_tablets()
self.storage = im.storage()
def test_size(self):
self.assertEqual(self.storage.tablets_size(), 0)
im.add_tablet("tag0", 100)
self.assertEqual(self.storage.tablets_size(), 1)
for i in range(1, 11):
im.add_tablet("tag%d" % i, 100)
self.assertEqual(self.storage.tablets_size(), 11)
def test_timestamp(self):
print self.storage.timestamp()
def test_dir(self):
dir = "./1.txt"
self.storage.set_dir(dir)
self.assertEqual(dir, self.storage.dir())
def test_human_readable_buffer(self):
print self.storage.human_readable_buffer()
class TabletTester(unittest.TestCase):
def setUp(self):
im.clear_tablets()
self.tablet = im.add_tablet("tag101", 20)
def test_human_readable_buffer(self):
print self.tablet.human_readable_buffer()
def test_scalar(self):
scalar = self.tablet.as_float_scalar()
py_captions = ["train", "test"]
step_ids = [10, 20, 30]
py_records = [[0.1, 0.2], [0.2, 0.3], [0.3, 0.4]]
scalar.set_captions(py_captions)
for i in range(len(py_records)):
scalar.add_record(step_ids[i], py_records[i])
records = scalar.get_records()
ids = scalar.get_ids()
for i in range(len(py_records)):
self.assertTrue(np.isclose(py_records[i], records[i]).all())
self.assertEqual(step_ids[i], ids[i])
class ImTester(unittest.TestCase):
def test_persist(self):
im.clear_tablets()
tablet = im.add_tablet("tab0", 111)
self.assertEqual(im.storage().tablets_size(), 1)
im.storage().set_dir("./1")
im.persist_to_disk()
if __name__ == '__main__':
unittest.main()
#ifndef VISUALDL_UTILS_CONCURRENCY_H
#define VISUALDL_UTILS_CONCURRENCY_H
#include <glog/logging.h>
#include <chrono>
#include <memory>
#include <thread>
#include <vector>
namespace visualdl {
namespace cc {
/*
* Run a task every `duration` milliseconds.
* Each evoke will start a thread to do this asynchronously.
*/
struct PeriodExector {
using task_t = std::function<bool()>;
void Quit() {
// TODO use some conditonal variable to help quit immediately.
// std::this_thread::sleep_for(std::chrono::milliseconds(200));
quit = true;
}
void Start() { quit = false; }
void operator()(task_t&& task, int msec) {
const int interval = 500;
auto task_wrapper = [=] {
while (!quit) {
// task failed
if (!task()) break;
// if the program is terminated, quit while as soon as possible.
// this is just trick, but should works.
if (msec > 1000) {
int i;
for (i = 0; i < msec / interval; i++) {
if (quit) break;
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
}
std::this_thread::sleep_for(
std::chrono::milliseconds(msec - i * interval));
if (quit) break;
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
}
}
LOG(INFO) << "quit concurrent job";
};
threads_.emplace_back(std::thread(std::move(task_wrapper)));
msec_ = msec;
}
~PeriodExector() {
Quit();
for (auto& t : threads_) {
if (t.joinable()) {
t.join();
}
}
}
private:
bool quit = false;
std::vector<std::thread> threads_;
int msec_;
};
} // namespace cc
} // namespace visualdl
#endif
#ifndef VISUALDL_UTILS_FILESYSTEM_H
#define VISUALDL_UTILS_FILESYSTEM_H
#include <google/protobuf/text_format.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
namespace visualdl {
namespace fs {
template <typename T>
std::string Serialize(const T& proto, bool human_readable = false) {
if (human_readable) {
std::string buffer;
google::protobuf::TextFormat::PrintToString(proto, &buffer);
return buffer;
}
return proto.SerializeAsString();
}
template <typename T>
bool DeSerialize(T* proto, const std::string buf, bool human_readable = false) {
// NOTE human_readable not valid
if (human_readable) {
return google::protobuf::TextFormat::ParseFromString(buf, proto);
}
return proto->ParseFromString(buf);
}
template <typename T>
bool SerializeToFile(const T& proto, const std::string& path) {
std::ofstream file(path, std::ios::binary);
return proto.SerializeToOstream(&file);
}
template <typename T>
bool DeSerializeFromFile(T* proto, const std::string& path) {
std::ifstream file(path, std::ios::binary);
CHECK(file.is_open()) << "open " << path << " failed";
return proto->ParseFromIstream(&file);
}
void TryMkdir(const std::string& dir) {
VLOG(1) << "try to mkdir " << dir;
struct stat st = {0};
if (stat(dir.c_str(), &st) == -1) {
::mkdir(dir.c_str(), 0700);
}
}
// Create a path by recursively create directries
void TryRecurMkdir(const std::string& path) {
// split path by '/'
for (int i = 1; i < path.size() - 1; i++) {
if (path[i] == '/') {
auto dir = path.substr(0, i + 1);
TryMkdir(dir);
}
}
// the last level
TryMkdir(path);
}
inline void Write(const std::string& path,
const std::string& buffer,
std::ios::openmode open_mode = std::ios::binary) {
VLOG(1) << "write to path " << path;
std::ofstream file(path, open_mode);
CHECK(file.is_open()) << "failed to open " << path;
file.write(buffer.c_str(), buffer.size());
file.close();
}
inline std::string Read(const std::string& path,
std::ios::openmode open_mode = std::ios::binary) {
VLOG(1) << "read from path " << path;
std::string buffer;
std::ifstream file(path, open_mode | std::ios::ate);
CHECK(file.is_open()) << "failed to open " << path;
size_t size = file.tellg();
file.seekg(0);
buffer.resize(size);
file.read(&buffer[0], size);
return buffer;
}
} // namespace fs
} // namespace visualdl
#endif // VISUALDL_BACKEND_UTILS_FILESYSTEM_H
#ifndef VISUALDL_UTILS_LOG_H
#define VISUALDL_UTILS_LOG_H
#include <stdexcept>
namespace visualdl {
namespace log {
class NotImplementedException : public std::logic_error {
public:
NotImplementedException() : std::logic_error{"Function not implemented"} {}
};
} // namespace log
} // namespace visualdl
#endif
#include "visualdl/utils/concurrency.h"
#include <glog/logging.h>
#include <gtest/gtest.h>
namespace visualdl {
int counter = 0;
TEST(concurrency, test) {
cc::PeriodExector executor;
cc::PeriodExector::task_t task = [&]() {
LOG(INFO) << "Hello " << counter++;
if (counter > 5) return false;
return true;
};
executor(std::move(task), 200);
}
} // namespace visualdl
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册