No ability in Dali for remote interactive control and performance monitoring ( via network).
Usage, apply patch then $nc localhost 3031 (type help)
Or connect using Stage Hand tool.
To perform a scene dump to a JSON file ( for black box testing )
echo "dump_scene" | nc -q4 adress port > scene.json
Change-Id: Ib43e547b931b66dde91d3f1bb011d2e7a90879fc
Signed-off-by: Nick Holland <nick.holland@partner.samsung.com>
const unsigned int DEFAULT_STATISTICS_LOG_FREQUENCY = 2;
}
EnvironmentOptions::EnvironmentOptions()
-: mFpsFrequency(0),
+: mNetworkControl(0),
+ mFpsFrequency(0),
mUpdateStatusFrequency(0),
mPerformanceStatsLevel(0),
mPerformanceStatsFrequency( DEFAULT_STATISTICS_LOG_FREQUENCY),
}
void EnvironmentOptions::SetLogOptions( const Dali::Integration::Log::LogFunction& logFunction,
+ unsigned int networkControl,
unsigned int logFrameRateFrequency,
unsigned int logupdateStatusFrequency,
unsigned int logPerformanceStats,
unsigned int logPanGestureLevel )
{
mLogFunction = logFunction;
+ mNetworkControl = networkControl;
mFpsFrequency = logFrameRateFrequency;
mUpdateStatusFrequency = logupdateStatusFrequency;
mPerformanceStatsLevel = logPerformanceStats;
Dali::Integration::Log::UninstallLogFunction();
}
+unsigned int EnvironmentOptions::GetNetworkControlMode() const
+{
+ return mNetworkControl;
+}
unsigned int EnvironmentOptions::GetFrameRateLoggingFrequency() const
{
return mFpsFrequency;
bool EnvironmentOptions::PerformanceServerRequired() const
{
return ( (GetPerformanceStatsLoggingOptions() > 0) ||
- ( GetPerformanceTimeStampOutput() > 0 ) );
+ ( GetPerformanceTimeStampOutput() > 0 ) ||
+ ( GetNetworkControlMode() > 0) );
}
} // Adaptor
/**
* @param logFunction logging function
+ * @param networkControl whether network control is enabled
* @param logFilterOptions bitmask of the logging options defined in intergration/debug.h (e.g.
* @param logFrameRateFrequency frequency of how often FPS is logged out (e.g. 0 = off, 2 = every 2 seconds).
* @param logupdateStatusFrequency frequency of how often the update status is logged in number of frames
* @param logPanGestureLevel pan-gesture logging, 0 = disabled, 1 = enabled
*/
void SetLogOptions( const Dali::Integration::Log::LogFunction& logFunction,
+ unsigned int networkControl,
unsigned int logFrameRateFrequency,
unsigned int logupdateStatusFrequency,
unsigned int logPerformanceStats,
void UnInstallLogFunction() const;
/**
+ * @return whether network control is enabled or not ( 0 = off, 1 = on )
+ */
+ unsigned int GetNetworkControlMode() const;
+
+ /**
* @return frequency of how often FPS is logged out (e.g. 0 = off, 2 = every 2 seconds).
*/
unsigned int GetFrameRateLoggingFrequency() const;
private:
+ unsigned int mNetworkControl; ///< whether network control is enabled
unsigned int mFpsFrequency; ///< how often fps is logged out in seconds
unsigned int mUpdateStatusFrequency; ///< how often update status is logged out in frames
unsigned int mPerformanceStatsLevel; ///< performance statistics logging bitmask
*/
#define DALI_ENV_PERFORMANCE_TIMESTAMP_OUTPUT "DALI_PERFORMANCE_TIMESTAMP_OUTPUT"
+/**
+ * Allow control and monitoring of DALi via the network
+ */
+#define DALI_ENV_NETWORK_CONTROL "DALI_NETWORK_CONTROL"
+
// environment variable for enabling/disabling fps tracking
#define DALI_ENV_FPS_TRACKING "DALI_FPS_TRACKING"
$(base_adaptor_src_dir)/vsync-notifier.cpp \
$(base_adaptor_src_dir)/performance-logging/frame-time-stamp.cpp \
$(base_adaptor_src_dir)/environment-options.cpp \
+ $(base_adaptor_src_dir)/performance-logging/networking/network-performance-protocol.cpp \
+ $(base_adaptor_src_dir)/performance-logging/networking/network-performance-client.cpp \
+ $(base_adaptor_src_dir)/performance-logging/networking/network-performance-server.cpp \
+ $(base_adaptor_src_dir)/performance-logging/networking/event/automation.cpp \
$(base_adaptor_src_dir)/performance-logging/frame-time-stats.cpp \
$(base_adaptor_src_dir)/performance-logging/performance-marker.cpp \
$(base_adaptor_src_dir)/performance-logging/statistics/stat-context.cpp \
// INTERNAL INCLUDES
#include <base/interfaces/egl-factory-interface.h>
-#include <base/interfaces/trigger-event-interface.h>
+#include <base/interfaces/trigger-event-factory-interface.h>
+#include <base/interfaces/socket-factory-interface.h>
#include <base/interfaces/performance-interface.h>
#include <base/interfaces/vsync-monitor-interface.h>
#include <base/interfaces/kernel-trace-interface.h>
virtual TriggerEventInterface& GetTriggerEventInterface() = 0;
/**
+ * @return trigger event factory interface
+ */
+ virtual TriggerEventFactoryInterface& GetTriggerEventFactoryInterface() = 0;
+
+ /**
+ * @return socket factory interface
+ */
+ virtual SocketFactoryInterface& GetSocketFactoryInterface() = 0;
+
+ /**
* @return render surface
*/
virtual RenderSurface* GetRenderSurfaceInterface() = 0;
enum TimeStampOutput
{
NO_TIME_STAMP_OUTPUT = 0,
- OUTPUT_DALI_LOG = 1 << 0, ///< Bit 1 (1), log markers to DALi log
- OUTPUT_KERNEL_TRACE = 1 << 1, ///< Bit 2 (2), log makers to kernel trace
- OUTPUT_SYSTEM_TRACE = 1 << 2, ///< Bit 3 (4), log markers to system trace
- OUTPUT_NETWORK = 1 << 3, ///< Bit 4 (8), log markers to network client
+ OUTPUT_DALI_LOG = 1 << 0, ///< Bit 0 (1), log markers to DALi log
+ OUTPUT_KERNEL_TRACE = 1 << 1, ///< Bit 1 (2), log makers to kernel trace
+ OUTPUT_SYSTEM_TRACE = 1 << 2, ///< Bit 2 (4), log markers to system trace
+ OUTPUT_NETWORK = 1 << 3, ///< Bit 3 (8), log markers to network client
};
/**
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_BASE_SOCKET_FACTORY_INTERFACE_H__
+#define __DALI_INTERNAL_ADAPTOR_BASE_SOCKET_FACTORY_INTERFACE_H__
+
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <base/interfaces/socket-interface.h>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+
+/**
+ * @brief abstract class to create and destroy sockets
+ */
+class SocketFactoryInterface
+{
+public:
+
+ /**
+ * @brief Create a new socket
+ * @param protocol network protocol
+ * @return true on success, false on failure
+ */
+ virtual SocketInterface* NewSocket( SocketInterface::Protocol protocol ) = 0;
+
+ /**
+ * @brief destroy a socket
+ * @param[in] socket socket to destroy
+ */
+ virtual void DestroySocket( SocketInterface* socket ) = 0;
+
+protected:
+
+ /**
+ * @brief Constructor
+ */
+ SocketFactoryInterface( )
+ {
+ }
+
+ /**
+ * @brief Virtual destructor
+ */
+ virtual ~SocketFactoryInterface()
+ {
+ }
+
+private:
+
+ // Undefined copy constructor.
+ SocketFactoryInterface( const SocketFactoryInterface& );
+
+ // Undefined assignment operator.
+ SocketFactoryInterface& operator=( const SocketFactoryInterface& );
+
+};
+
+
+
+} // Adaptor
+} // Internal
+} // Dali
+
+#endif // __DALI_INTERNAL_ADAPTOR_BASE_SOCKET_FACTORY_INTERFACE_H__
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_BASE_SOCKET_INTERFACE_H__
+#define __DALI_INTERNAL_ADAPTOR_BASE_SOCKET_INTERFACE_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+
+/**
+ * @brief Abstract socket interface.
+ * The typical usage is:
+ *
+ * @code
+ *
+ * SocketFactory::NewSocket( SocketInterface::TCP );
+ * socket->ReuseAddress( true );
+ * socket->Bind( port );
+ * socket->Listen();
+ *
+ * ret = socket->Select(); // call socket->ExitSelect to break from select from another thread
+ * if ( ret == DATA_AVAILABLE )
+ * {
+ * socket->Read( ...);
+ * }
+ * socket->Close();
+ *
+ * @endcode
+ *
+ */
+class SocketInterface
+{
+public:
+
+ /**
+ * @brief Protocol type
+ */
+ enum Protocol
+ {
+ TCP, ///< Reliable, connection oriented
+ UDP, ///< Connection less, no guarantees of packet delivery, ordering
+ };
+
+ /**
+ * @brief check is a socket is open
+ * @return true if the socket is currently open
+ */
+ virtual bool SocketIsOpen() const = 0;
+
+ /**
+ * @brief CloseSocket
+ * @return true on success, false on failure
+ */
+ virtual bool CloseSocket() = 0;
+
+ /**
+ * @brief Socket bind, associate a local address with a socket ( normally uses AF_INET + INADDR_ANY)
+ * @param[in] port network port
+ * @return true on success, false on failure
+ */
+ virtual bool Bind( uint16_t port ) = 0;
+
+ /**
+ * @brief Indicate a willingness to accept incoming connection requests
+ * @param[in] backlog maximum length of the queue of pending connections.
+ * @return true on success, false on failure
+ */
+ virtual bool Listen( int blacklog) = 0;
+
+ /**
+ * @brief Wait for connection request and make connection
+ * @return new client socket
+ */
+ virtual SocketInterface* Accept() const = 0;
+
+ /**
+ * @brief Select return values
+ */
+ enum SelectReturn
+ {
+ DATA_AVAILABLE, ///< Data is available to read
+ QUIT, ///< ExitSelect() has been called on the socket
+ ERROR ///< Socket error
+ };
+
+ /**
+ * @brief Waits for an event to occur (data available / error)
+ * Returns when
+ * - data has been sent to the socket
+ * - client has closed the connection ( Read will return 0 bytes)
+ * - ExitSelect has been called (returns QUIT)
+ * - There is an error (returns ERROR)
+ * @return DATA_AVAILABLE if data is available
+ */
+ virtual SelectReturn Select() = 0;
+
+ /**
+ * @brief To be called from a separate thread to break out of select
+ */
+ virtual void ExitSelect() = 0;
+
+ /**
+ * @brief Read data from the socket
+ * @param[out] buffer data
+ * @param[in] bufferSizeInBytes buffer size in bytes
+ * @param[out] bytesRead number of bytes read
+ * @return true on success, false on failure
+ */
+ virtual bool Read( void* buffer, unsigned int bufferSizeInBytes, unsigned int& bytesRead ) = 0;
+
+ /**
+ * @brief Send data to the socket
+ * @param[in] buffer data to write
+ * @param[in] bufferSizeInBytes buffer size in write
+ * @return true on success, false on failure
+ */
+ virtual bool Write( const void* buffer, unsigned int bufferSizeInBytes ) = 0;
+
+ //
+ // Common socket options. Please add more as required.
+ // These should be wrappers around the setsockopt API
+
+ /**
+ * @brief Whether the SO_REUSEADDR is enabled or not.
+ * @param[in] reuse flag.
+ * @return true on success, false on failure
+ */
+ virtual bool ReuseAddress( bool reUse ) = 0;
+
+ /**
+ * @brief Socket buffer type
+ */
+ enum BufferType
+ {
+ SEND_BUFFER, ///< (SO_SNDBUF) Send buffer size
+ RECIEVE_BUFFER ///< (SO_RCVBUF) Size of buffer allocated to hold data arriving to the socket
+ };
+
+ /**
+ * @brief Set the send and recieve buffer sizes ( SO_SNDBUF, SO_RCVBUF )
+ * @param[in] type buffer type
+ * @param[in] size buffer size
+ * @return true on success, false on failure
+ */
+ virtual bool SetBufferSize( BufferType type, unsigned int size ) = 0;
+
+protected:
+
+ /**
+ * @brief Constructor
+ */
+ SocketInterface( )
+ {
+ }
+
+ /**
+ * @brief virtual destructor
+ */
+ virtual ~SocketInterface()
+ {
+ }
+
+private:
+
+ // Undefined copy constructor.
+ SocketInterface( const SocketInterface& );
+
+ // Undefined assignment operator.
+ SocketInterface& operator=( const SocketInterface& );
+
+};
+
+
+} // Adaptor
+} // Internal
+} // Dali
+
+#endif // __DALI_INTERNAL_ADAPTOR_BASE_SOCKET_INTERFACE_H__
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_CLIENT_SEND_DATA_INTERFACE_H__
+#define __DALI_INTERNAL_ADAPTOR_CLIENT_SEND_DATA_INTERFACE_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+/**
+ * @brief Abstract interface used to transmit data to a client
+ */
+class ClientSendDataInterface
+{
+public:
+
+ /**
+ * @brief Sends data to the client
+ * @param[in] data pointer to some data
+ * @param[in] bufferSizeInBytes how big the buffer is in bytes
+ * @param[in] clientId unique client id to send the data to
+ */
+ virtual void SendData( const char* const data, unsigned int bufferSizeInBytes, unsigned int clientId ) = 0;
+
+
+protected:
+
+ /**
+ * @brief Constructor
+ */
+ ClientSendDataInterface()
+ {
+ }
+
+ /**
+ * @brief Virtual Destructor
+ */
+ virtual ~ClientSendDataInterface()
+ {
+ }
+
+private:
+
+ // Undefined copy constructor.
+ ClientSendDataInterface( const ClientSendDataInterface& );
+
+ // Undefined assignment operator.
+ ClientSendDataInterface& operator=( const ClientSendDataInterface& );
+
+};
+
+
+} // namespace Internal
+
+} // namespace Adaptor
+
+} // namespace Dali
+
+#endif // __DALI_INTERNAL_ADAPTOR_CLIENT_SEND_DATA_INTERFACE_H__
--- /dev/null
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "automation.h"
+
+// EXTERNAL INCLUDES
+#include <sstream>
+#include <iomanip>
+#include <stdio.h>
+#include <dali/public-api/dali-core.h>
+#include <dali/integration-api/debug.h>
+
+
+namespace // un-named namespace
+{
+
+const unsigned int MAX_SET_PROPERTY_STRING_LENGTH = 256; ///< maximum length of a set property command
+
+class JsonPropertyValue
+{
+public:
+ JsonPropertyValue( const std::string& str )
+ {
+ std::size_t strLength = str.length();
+
+ mString.reserve( strLength );
+ for( std::size_t i = 0; i < strLength; ++i )
+ {
+ const char c = str[i];
+ if( (c != '[') && c != ']')
+ {
+ mString.push_back( c );
+ }
+ }
+
+ }
+ std::string GetString() const
+ {
+ return mString;
+ }
+ float GetFloat() const
+ {
+ return atof( mString.c_str() );
+ }
+ int GetInt()
+ {
+ return atoi( mString.c_str() );
+ }
+ bool GetBoolean()
+ {
+ return (GetInt() != 0);
+ }
+
+ Dali::Vector2 GetVector2()
+ {
+ Dali::Vector2 vec2;
+
+ int count = sscanf( mString.c_str(),"%f,%f",&vec2.x,&vec2.y );
+ if( count != 2 )
+ {
+ DALI_LOG_ERROR("Bad format");
+ }
+ return vec2;
+ }
+
+ Dali::Vector3 GetVector3()
+ {
+ Dali::Vector3 vec3;
+
+ int count = sscanf( mString.c_str(),"%f,%f,%f",&vec3.x,&vec3.y,&vec3.z );
+ if( count != 3 )
+ {
+ DALI_LOG_ERROR("Bad format");
+ }
+ return vec3;
+ }
+
+ Dali::Vector4 GetVector4()
+ {
+ Dali::Vector4 vec4;
+
+ int count = sscanf( mString.c_str(),"%f,%f,%f,%f", &vec4.x, &vec4.y, &vec4.z, &vec4.w );
+ if( count != 4 )
+ {
+ DALI_LOG_ERROR("Bad format");
+ }
+ return vec4;
+ }
+
+private:
+ std::string mString;
+
+};
+
+void SetProperty( Dali::Handle handle, int propertyId, JsonPropertyValue& propertyValue )
+{
+ Dali::Property::Type type = handle.GetPropertyType( propertyId );
+ switch( type )
+ {
+ case Dali::Property::FLOAT:
+ {
+ float val = propertyValue.GetFloat();
+ handle.SetProperty( propertyId, Dali::Property::Value( val ) );
+ break;
+ }
+ case Dali::Property::INTEGER:
+ {
+ int val = propertyValue.GetInt();
+ handle.SetProperty( propertyId, Dali::Property::Value( val ) );
+ break;
+ }
+ case Dali::Property::BOOLEAN:
+ {
+ bool val = propertyValue.GetBoolean();
+ handle.SetProperty( propertyId, Dali::Property::Value( val ) );
+ break;
+ }
+ case Dali::Property::STRING:
+ {
+ std::string str = propertyValue.GetString();
+ handle.SetProperty( propertyId, Dali::Property::Value( str ) );
+ break;
+ }
+ case Dali::Property::VECTOR2:
+ {
+ Dali::Vector2 val = propertyValue.GetVector2();
+ handle.SetProperty( propertyId, Dali::Property::Value( val ) );
+ break;
+ }
+ case Dali::Property::VECTOR3:
+ {
+ Dali::Vector3 val = propertyValue.GetVector3();
+ handle.SetProperty( propertyId, Dali::Property::Value( val ) );
+ break;
+ }
+ case Dali::Property::VECTOR4:
+ {
+ Dali::Vector4 val = propertyValue.GetVector4();
+ handle.SetProperty( propertyId, Dali::Property::Value( val ) );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+int SetProperties( const std::string& setPropertyMessage )
+{
+ std::istringstream iss( setPropertyMessage );
+ std::string token;
+ getline( iss, token, '|' ); // swallow command name
+ while( getline( iss, token, '|' ) )
+ {
+ std::string actorId, propName, propValue;
+ if( token.compare( "---" ) != 0 )
+ {
+ std::istringstream propss( token );
+ getline( propss, actorId, ';' );
+ getline( propss, propName, ';' );
+ getline( propss, propValue );
+
+ Dali::Actor root = Dali::Stage::GetCurrent().GetRootLayer();
+ int id = atoi( actorId.c_str() );
+ Dali::Actor a = root.FindChildById( id );
+ if( a )
+ {
+ // lookup by name for custom properties
+ int propId = a.GetPropertyIndex( propName );
+ if( propId > 0 )
+ {
+ JsonPropertyValue pv( propValue );
+ SetProperty( a, propId, pv );
+ }
+
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+}; // un-named namespace
+
+inline std::string Quote( const std::string& in )
+{
+ return (std::string( "\"" ) + in + std::string( "\"" ));
+}
+
+template<class T>
+std::string ToString( T i )
+{
+ std::stringstream ss;
+ std::string s;
+ ss << i;
+ s = ss.str();
+
+ return s;
+}
+
+
+// currently rotations are output in Euler format ( may change)
+void AppendPropertyNameAndValue( Dali::Handle handle, int propertyIndex, std::ostringstream& outputStream)
+{
+ // get the property name and the value as a string
+ std::string propertyName( handle.GetPropertyName( propertyIndex ) );
+ Dali::Property::Value value = handle.GetProperty( propertyIndex );
+
+ // Apply quotes around the property name and the value.. "color", "1.3, 3.4, 2.6"
+ outputStream << "\"" << propertyName << "\"" << ",";
+ outputStream << "\"" << value << "\"";
+
+}
+
+bool ExcludeProperty( int propIndex )
+{
+ return (propIndex == Dali::Actor::Property::NAME ||
+
+ // all of these are repeat properties of values in vectors....
+ // We don't really need these in the UI
+ propIndex == Dali::Actor::Property::ANCHOR_POINT_X || propIndex == Dali::Actor::Property::ANCHOR_POINT_Y || propIndex == Dali::Actor::Property::ANCHOR_POINT_Z || propIndex == Dali::Actor::Property::PARENT_ORIGIN_X
+ || propIndex == Dali::Actor::Property::PARENT_ORIGIN_Y || propIndex == Dali::Actor::Property::PARENT_ORIGIN_Z || propIndex == Dali::Actor::Property::COLOR_RED || propIndex == Dali::Actor::Property::COLOR_GREEN
+ || propIndex == Dali::Actor::Property::COLOR_BLUE || propIndex == Dali::Actor::Property::COLOR_ALPHA|| propIndex == Dali::Actor::Property::POSITION_X || propIndex == Dali::Actor::Property::POSITION_Y
+ || propIndex == Dali::Actor::Property::POSITION_Z|| propIndex == Dali::Actor::Property::SIZE_WIDTH|| propIndex == Dali::Actor::Property::SIZE_HEIGHT || propIndex == Dali::Actor::Property::SCALE_X || propIndex == Dali::Actor::Property::SCALE_Y
+ || propIndex == Dali::Actor::Property::SCALE_Z || propIndex == Dali::Actor::Property::SIZE_DEPTH);
+}
+
+std::string DumpJson( Dali::Actor actor, int level )
+{
+ // All the information about this actor
+ std::ostringstream msg;
+ msg << "{ " << Quote( "Name" ) << " : " << Quote( actor.GetName() ) << ", " << Quote( "level" ) << " : " << level << ", " << Quote( "id" ) << " : " << actor.GetId() << ", " << Quote( "IsVisible" )
+ << " : " << actor.IsVisible() << ", " << Quote( "IsSensitive" ) << " : " << actor.IsSensitive();
+
+ msg << ", " << Quote( "properties" ) << ": [ ";
+
+ Dali::Property::IndexContainer indices;
+ actor.GetPropertyIndices( indices );
+
+ Dali::Property::IndexContainer::iterator iter = indices.begin();
+ int numCustom = 0;
+ for( ; iter != indices.end() ; iter++ )
+ {
+ int i = *iter;
+ if( !ExcludeProperty( i ) )
+ {
+ if( numCustom++ != 0 )
+ {
+ msg << ", ";
+ }
+ msg << "[";
+
+ AppendPropertyNameAndValue( actor, i,msg );
+
+ msg << "]";
+ }
+ }
+ msg << "]";
+ msg << ", " << Quote( "children" ) << " : [ ";
+
+ // Recursively dump all the children as well
+ for( unsigned int i = 0 ; i < actor.GetChildCount() ; ++i )
+ {
+ if( i )
+ {
+ msg << " , ";
+ }
+ msg << DumpJson( actor.GetChildAt( i ), level + 1 );
+ }
+ msg << "] }";
+
+ return msg.str();
+}
+
+std::string GetActorTree()
+{
+ Dali::Actor actor = Dali::Stage::GetCurrent().GetRootLayer();
+ std::string str = DumpJson( actor, 0 );
+ return str;
+}
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+namespace Automation
+{
+
+void SetProperty( const std::string& message )
+{
+ // check the set property length is within range
+ if( message.length() > MAX_SET_PROPERTY_STRING_LENGTH )
+ {
+ DALI_LOG_ERROR("SetProperty message length too long, size = %ul\n", message.length());
+ return;
+ }
+
+ SetProperties( message );
+}
+
+void DumpScene( unsigned int clientId, ClientSendDataInterface* sendData )
+{
+ char buf[32];
+ std::string json = GetActorTree();
+ int length = json.length();
+ sprintf( buf, "%d\n", length );
+ std::string header( buf );
+ json = buf + json;
+ sendData->SendData( json.c_str(), json.length(), clientId );
+}
+
+} // namespace Automation
+
+} // namespace Internal
+
+} // namespace Adaptor
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_AUTOMATION_H__
+#define __DALI_INTERNAL_ADAPTOR_AUTOMATION_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+// EXTERNAL INCLUDES
+#include <string>
+
+// INTERNAL INCLUDES
+#include <base/performance-logging/networking/client-send-data-interface.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+/**
+ * @brief The automation functions allow a way to control Dali via a network socket.
+ *
+ * The functions must be called from the event thread only.
+ *
+ * Any functions which require a response to be sent back to the network client
+ * use the ClientSendDataInterface interface.
+
+ * E.g.
+ * Dali network client thread <---- "dump_scene" from network
+ * Dali main thread "json data" -----> network
+ *
+ */
+namespace Automation
+{
+
+/**
+ * @brief Sets properties on an Actor.
+ * No ClientSendDataInterface required, as no response is sent back
+ * @param[in] message set property message
+ */
+void SetProperty( const std::string& message );
+
+
+/**
+ * @brief Dumps the actor tree to the client
+ * @param[in] clientId unique network client id
+ * @param[in] sendData interface to transmit data to the client
+ */
+void DumpScene( unsigned int clientId, ClientSendDataInterface* sendData );
+
+
+} // namespace Automation
+
+} // namespace Internal
+
+} // namespace Adaptor
+
+} // namespace Dali
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "network-performance-client.h"
+
+// EXTERNAL INCLUDES
+#include <stdio.h>
+#include <string>
+
+// INTERNAL INCLUDES
+#include <base/interfaces/socket-interface.h>
+#include <base/performance-logging/networking/network-performance-protocol.h>
+#include <base/performance-logging/networking/event/automation.h>
+
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+namespace
+{
+const char UNKNOWN_CMD[]= "Command or parameter invalid, type help for list of commands\n";
+
+
+/**
+ * helper class to store data along with the automation callback.
+ */
+class AutomationCallback: public CallbackBase
+{
+public:
+
+ /**
+ * instead of using templates, or having different callback classes for each callback
+ * we use a command id that decides which static function to call on the Automation class.
+ */
+ enum CommandId
+ {
+ UNKNOWN_COMMAND,
+ SET_PROPERTY,
+ DUMP_SCENE
+ };
+
+ AutomationCallback( unsigned int clientId, ClientSendDataInterface& sendDataInterface )
+ :CallbackBase( reinterpret_cast< void* >( this ),
+ NULL, // we get the dispatcher to call function directly
+ reinterpret_cast< CallbackBase::Dispatcher>( &AutomationCallback::Dispatcher) ),
+ mSendDataInterface( sendDataInterface ),
+ mCommandId( UNKNOWN_COMMAND ),
+ mClientId( clientId )
+ {}
+
+ void AssignSetPropertyCommand( std::string setPropertyCommand )
+ {
+ mCommandId = SET_PROPERTY;
+ mPropertyCommand = setPropertyCommand;
+ }
+ void AssignDumpSceneCommand()
+ {
+ mCommandId = DUMP_SCENE;
+ }
+
+ void RunCallback()
+ {
+ switch( mCommandId )
+ {
+ case SET_PROPERTY:
+ {
+ Automation::SetProperty( mPropertyCommand );
+ break;
+ }
+ case DUMP_SCENE:
+ {
+ Automation::DumpScene( mClientId, &mSendDataInterface);
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_DEBUG( 0 && "Unknown command");
+ break;
+ }
+ }
+ }
+ static void Dispatcher( CallbackBase& base )
+ {
+ AutomationCallback& automationCallback( static_cast< AutomationCallback& >( base) );
+ automationCallback.RunCallback();
+ }
+
+private:
+
+ std::string mPropertyCommand; ///< property command
+ ClientSendDataInterface& mSendDataInterface; ///< Abstract client send data interface
+ CommandId mCommandId; ///< command id
+ const unsigned int mClientId; ///< client id
+};
+
+} // unnamed namespace
+
+NetworkPerformanceClient::NetworkPerformanceClient( SocketInterface *socket,
+ unsigned int clientId,
+ TriggerEventFactoryInterface& triggerEventFactory,
+ ClientSendDataInterface& sendDataInterface,
+ SocketFactoryInterface& socketFactory )
+: mSocket( socket ),
+ mMarkerBitmask( PerformanceMarker::FILTERING_DISABLED ),
+ mTriggerEventFactory( triggerEventFactory ),
+ mSendDataInterface( sendDataInterface ),
+ mSocketFactoryInterface( socketFactory ),
+ mClientId( clientId ),
+ mConsoleClient(false)
+{
+
+}
+
+NetworkPerformanceClient::~NetworkPerformanceClient()
+{
+ if( mSocket->SocketIsOpen() )
+ {
+ mSocket->CloseSocket();
+ }
+ mSocketFactoryInterface.DestroySocket( mSocket );
+}
+
+unsigned int NetworkPerformanceClient::GetId() const
+{
+ return mClientId;
+}
+
+SocketInterface& NetworkPerformanceClient::GetSocket()
+{
+ return *mSocket;
+}
+
+bool NetworkPerformanceClient::WriteSocket( const void* buffer, unsigned int bufferSizeInBytes )
+{
+ return mSocket->Write( buffer, bufferSizeInBytes );
+}
+
+bool NetworkPerformanceClient::TransmitMarker( const PerformanceMarker& marker, const char* const description )
+{
+ if( ! marker.IsFilterEnabled( mMarkerBitmask ) )
+ {
+ return true;
+ }
+ if( mConsoleClient )
+ {
+ // write out the time stamp
+ char buffer[64];
+ int size = snprintf( buffer, sizeof(buffer),"%d.%06d (seconds), %s\n",
+ marker.GetTimeStamp().seconds,
+ marker.GetTimeStamp().microseconds,
+ description );
+
+ return mSocket->Write( buffer, size );
+
+ }
+
+ // todo serialize the data
+ return false;
+}
+
+void NetworkPerformanceClient::ExitSelect()
+{
+ mSocket->ExitSelect();
+}
+
+
+void NetworkPerformanceClient::ProcessCommand( char* buffer, unsigned int bufferSizeInBytes )
+{
+ // if connected via console, then strip off the carriage return, and switch to console mode
+ if( buffer[ bufferSizeInBytes - 1] == '\n')
+ {
+ buffer[ bufferSizeInBytes - 1] = 0;
+ mConsoleClient = true;
+ }
+ unsigned int param(0);
+ std::string stringParam;
+ PerformanceProtocol::CommandId commandId( PerformanceProtocol::UNKNOWN_COMMAND );
+
+ bool ok = PerformanceProtocol::GetCommandId( buffer, bufferSizeInBytes, commandId, param, stringParam );
+ if( !ok )
+ {
+ WriteSocket( UNKNOWN_CMD, sizeof(UNKNOWN_CMD) );
+ return;
+ }
+ std::string response;
+
+ switch( commandId )
+ {
+ case PerformanceProtocol::HELP_MESSAGE:
+ {
+ response = PerformanceProtocol::GetHelpMessage();
+ break;
+ }
+
+ case PerformanceProtocol::ENABLE_TIME_MARKER_BIT_MASK:
+ {
+ mMarkerBitmask = static_cast< PerformanceMarker::MarkerFilter >( param );
+ response = "enable time marker ";
+ break;
+ }
+
+ case PerformanceProtocol::DUMP_SCENE_GRAPH:
+ {
+ // this needs to be run on the main thread, use the trigger event....
+ AutomationCallback* callback = new AutomationCallback( mClientId, mSendDataInterface );
+ callback->AssignDumpSceneCommand();
+
+ // create a trigger event that automatically deletes itself after the callback has run in the main thread
+ TriggerEventInterface *interface = mTriggerEventFactory.CreateTriggerEvent( callback, TriggerEventInterface::DELETE_AFTER_TRIGGER );
+
+ // asynchronous call, the call back will be run sometime later on the main thread
+ interface->Trigger();
+ break;
+ }
+
+ case PerformanceProtocol::SET_PROPERTIES:
+ {
+ // this needs to be run on the main thread, use the trigger event....
+ AutomationCallback* callback = new AutomationCallback( mClientId, mSendDataInterface );
+ callback->AssignSetPropertyCommand( stringParam );
+
+ // create a trigger event that automatically deletes itself after the callback has run in the main thread
+ TriggerEventInterface *interface = mTriggerEventFactory.CreateTriggerEvent( callback, TriggerEventInterface::DELETE_AFTER_TRIGGER );
+
+ // asynchronous call, the call back will be run sometime later on the main thread
+ interface->Trigger();
+ break;
+ }
+
+ case PerformanceProtocol::LIST_METRICS_AVAILABLE:
+ case PerformanceProtocol::ENABLE_METRIC:
+ case PerformanceProtocol::DISABLE_METRIC:
+ {
+ response="Metrics currently not supported";
+ break;
+ }
+ default:
+ {
+ response = UNKNOWN_CMD;
+ break;
+ }
+ }
+ if( ! response.empty() )
+ {
+ // add a carriage return for console clients
+ if( mConsoleClient )
+ {
+ response+="\n";
+ }
+ WriteSocket( response.c_str(), response.length() );
+ }
+}
+
+
+
+} // namespace Internal
+
+} // namespace Adaptor
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_CLIENT_H__
+#define __DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_CLIENT_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <base/performance-logging/performance-marker.h>
+#include <base/interfaces/trigger-event-factory-interface.h>
+#include <base/performance-logging/networking/client-send-data-interface.h>
+#include <base/interfaces/socket-factory-interface.h>
+
+namespace boost
+{
+class thread;
+}
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+/**
+ * @brief Network Performance client
+ *
+ * Every time a client connects to Dali, a NetworkPerformanceClient object is created.
+ * It is responsible for processing incoming commands, and storing the client state
+ * (e.g. what performance markers it wants).
+ *
+ * Certain commands such as dump-scene need to be run on the main Dali event thread.
+ * To achieve this, a trigger event is used which executes a function on the main thread.
+ * The sendDataInterface is then used with the client id to transmit the data to the client.
+ * The reason for using a client id is because the client
+ * can be deleted in between receiving a command and sending a response.
+ * E.g.
+ * NetworkPerformanceClient (own thread, id 5) <--- Dump Scene Command
+ * delete NetworkPerformanceClient <--- Connection closed
+ * MainThread. Send scene data to client 5. Client 5 has been deleted so don't send the data.
+ *
+ */
+class NetworkPerformanceClient
+{
+public:
+
+ /**
+ * @brief Constructor
+ * @param socket socket interface
+ * @param clientId unique client id
+ * @param triggerEventFactory used to create trigger events
+ * @param sendDataInterface used to send data to the socket from main thread
+ * @param SocketFactoryInterface used to delete the socket when the client is destroyed
+ */
+ NetworkPerformanceClient( SocketInterface *socket,
+ unsigned int clientId,
+ TriggerEventFactoryInterface& triggerEventFactory,
+ ClientSendDataInterface& sendDataInterface,
+ SocketFactoryInterface& socketFactory );
+
+ /**
+ * @brief Destructor
+ */
+ ~NetworkPerformanceClient();
+
+ /**
+ * @return client unique id
+ */
+ unsigned int GetId() const;
+
+ /**
+ * @return socket interface
+ */
+ SocketInterface& GetSocket();
+
+ /**
+ * @brief Write data to a socket. Can be called from any thread
+ * @copydoc Dali::SocketInterface::Send
+ */
+ bool WriteSocket( const void* buffer, unsigned int bufferSizeInBytes );
+
+ /**
+ * @brief Process a command
+ * @param buffer pointer to command data
+ * @param bufferSizeInBytes how big the buffer is in bytes
+ */
+ void ProcessCommand( char* buffer, unsigned int bufferSizeInBytes );
+
+ /**
+ * @brief Write a marker to the socket, if this client is filtering this marker.
+ * @param marker
+ */
+ bool TransmitMarker( const PerformanceMarker& marker, const char* const description );
+
+ /**
+ * @brief If the client is waiting inside a select statement, this will cause it
+ * to break out.
+ */
+ void ExitSelect();
+
+private:
+
+ SocketInterface* mSocket; ///< socket interface
+ PerformanceMarker::MarkerFilter mMarkerBitmask; ///< What markers are currently filtered
+ TriggerEventFactoryInterface& mTriggerEventFactory; ///< Trigger event factory
+ ClientSendDataInterface& mSendDataInterface; ///< used to send data to a client from the main event thread
+ SocketFactoryInterface& mSocketFactoryInterface; ///< used to delete the socket
+ unsigned int mClientId; ///< unique client id
+ bool mConsoleClient; ///< if connected via a console then all responses are in ASCII, not binary packed data.
+
+};
+
+
+} // namespace Internal
+
+} // namespace Adaptor
+
+} // namespace Dali
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+
+// CLASS HEADER
+#include "network-performance-protocol.h"
+
+// EXTERNAL INCLUDES
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace Dali
+{
+
+namespace PerformanceProtocol
+{
+
+namespace
+{
+
+/**
+ * Command parameter type
+ */
+enum PARAMETER_TYPE
+{
+ NO_PARAMS,
+ UNSIGNED_INT,
+ STRING
+};
+
+/**
+ * Command information structure
+ */
+struct CommandInfo
+{
+ CommandId cmdId;
+ CommandString cmdString;
+ PARAMETER_TYPE paramType;
+};
+
+/**
+ * Command lookup table
+ */
+CommandInfo CommandLookup[]=
+{
+ { HELP_MESSAGE , "help" ,NO_PARAMS },
+ { ENABLE_METRIC , "enable_metric" ,UNSIGNED_INT },
+ { DISABLE_METRIC , "disable_metric" ,UNSIGNED_INT },
+ { LIST_METRICS_AVAILABLE , "list_metrics" ,NO_PARAMS },
+ { ENABLE_TIME_MARKER_BIT_MASK, "set_marker", UNSIGNED_INT },
+ { DUMP_SCENE_GRAPH , "dump_scene" ,NO_PARAMS },
+ { SET_PROPERTIES , "set_properties" ,STRING },
+ { UNKNOWN_COMMAND , "unknown" ,NO_PARAMS }
+};
+const unsigned int CommandLookupLength = sizeof( CommandLookup ) /sizeof( CommandInfo );
+
+#define GREEN "\033[01;32m"
+#define NORMAL "\e[m"
+#define PARAM "\033[22;32m"
+#define YELLOW "\033[01;33m"
+
+const char* const helpMsg =
+ YELLOW
+ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ " Dali performance console \n"
+ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" NORMAL
+ GREEN " list_metrics " NORMAL " - list available metrics\n"
+ GREEN " enable_metric " PARAM " metricId" NORMAL " - enable a metric \n"
+ GREEN " disable_metric "PARAM " metricId" NORMAL " - disable a metric\n\n"
+ GREEN " set_marker " PARAM " value " NORMAL "-output Dali markers\n"
+ " : Bit 0 = V_SYNC (1)\n"
+ " : Bit 1 = Update task (2)\n"
+ " : Bit 2 = Render task (4) \n"
+ " : Bit 3 = Event Processing task (8)\n"
+ " : Bit 4 = SwapBuffers (16)\n"
+ " : Bit 5 = Life cycle events (32)\n"
+ " : Bit 6 = Resource event (64)\n"
+ "\n"
+ GREEN" set_properties "NORMAL " - set an actor property command. Format:\n\n"
+ GREEN" set_properties "PARAM"|ActorIndex;Property;Value|" NORMAL ", e.g: \n"
+ GREEN" set_properties " PARAM "|178;Size;[ 144.0, 144.0, 144.0 ]|178;Color;[ 1.0, 1,0, 1.0 ]|\n"
+ "\n"
+ GREEN " dump_scene" NORMAL " - dump the current scene in json format\n";
+
+} // un-named namespace
+
+bool GetCommandId( const char* const commandString, unsigned int lengthInBytes, CommandId& commandId, unsigned int& intParam, std::string& stringParam )
+{
+ commandId = UNKNOWN_COMMAND;
+ intParam = 0;
+
+ // the command list is small so just do a O(n) search for the commandID.
+ for( unsigned int i = 0 ; i < CommandLookupLength; ++i )
+ {
+ if( strncmp( commandString, CommandLookup[i].cmdString ,strlen(CommandLookup[i].cmdString )) == 0 )
+ {
+ commandId = CommandLookup[i].cmdId;
+
+ // if the command has a parameter read it
+ if( CommandLookup[i].paramType == UNSIGNED_INT)
+ {
+ int count = sscanf(commandString,"%*s %d",&intParam);
+ if( count != 1 )
+ {
+ // missing parameter
+ return false;
+ }
+ }
+ else if (CommandLookup[i].paramType == STRING )
+ {
+ char* charParam( NULL );
+ // allocates the character array
+ int count = sscanf(commandString,"%*s %ms",&charParam);
+ if( count != 1 )
+ {
+ // missing parameter
+ return false;
+ }
+ stringParam = std::string( charParam);
+ free(charParam);
+ }
+ return true;
+ }
+ }
+ // not found
+ return false;
+}
+
+bool GetCommandString( CommandId commandId, CommandString& commandString )
+{
+ for( unsigned int i = 0; i < CommandLookupLength; ++i)
+ {
+ if( CommandLookup[ i ].cmdId == commandId )
+ {
+ strncpy( commandString, CommandLookup[ i ].cmdString, strlen(CommandLookup[ i ].cmdString) );
+ return true;
+ }
+ }
+ strncpy( commandString, CommandLookup[ UNKNOWN_COMMAND ].cmdString, MAX_COMMAND_STRING_LENGTH);
+ return false;
+}
+
+const char* const GetHelpMessage()
+{
+ return helpMsg;
+}
+
+
+} // namespace PerformanceProtocol
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_PROTOCOL_H__
+#define __DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_PROTOCOL_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <string>
+
+namespace Dali
+{
+
+namespace PerformanceProtocol
+{
+
+const unsigned int MAX_COMMAND_STRING_LENGTH = 256; ///< maximum length of a command including null terminator
+
+/**
+ * @brief List of commands id's
+ */
+enum CommandId
+{
+ HELP_MESSAGE = 0, ///< help message
+ ENABLE_METRIC = 1, ///< enable metric
+ DISABLE_METRIC = 2, ///< disable metric
+ LIST_METRICS_AVAILABLE = 3, ///< list metrics that are available
+ ENABLE_TIME_MARKER_BIT_MASK = 4, ///< bit mask of time markers to enable
+ SET_PROPERTIES = 5, ///< set property
+ DUMP_SCENE_GRAPH = 6, ///< dump the scene graph
+ UNKNOWN_COMMAND = 4096
+};
+
+
+typedef char CommandString[ MAX_COMMAND_STRING_LENGTH ];
+
+/**
+ * @brief given a command id, get the command string
+ * @param[in] commandId command id
+ * @param[out] commandString command string
+ * @return true on success, false on failure
+ */
+bool GetCommandString( CommandId commandId, CommandString& commandString );
+
+/**
+ * @brief given a command string, get the command id and an integer parameter if it exists
+ * @param[in] commandString command string
+ * @param[in] lengthInBytes length of the string
+ * @param[out] commandId command id
+ * @param[out] intParam integer parameter
+ * @param[out] stringParam string parameter
+ * @param true on success, false on failure
+ */
+bool GetCommandId( const char* const commandString,
+ unsigned int lengthInBytes,
+ CommandId& commandId,
+ unsigned int& intParam,
+ std::string& stringParam);
+
+/**
+ * @return the protocol help message for console users
+ */
+const char* const GetHelpMessage();
+
+} // namespace PerformanceProtocol
+
+} // namespace Dali
+
+#endif //__DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_PROTOCOL_H__
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "network-performance-server.h"
+
+// EXTERNAL INCLUDES
+#include <boost/thread.hpp>
+
+// INTERNAL INCLUDES
+#include <base/performance-logging/performance-marker.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+namespace // un-named namespace
+{
+const unsigned int SERVER_PORT = 3031;
+const unsigned int MAXIMUM_PORTS_TO_TRY = 10; ///< if port in use, try up to SERVER_PORT + 10
+const unsigned int CONNECTION_BACKLOG = 2; ///< maximum length of the queue of pending connections.
+const unsigned int SOCKET_READ_BUFFER_SIZE = 4096;
+typedef Vector< NetworkPerformanceClient*> ClientList;
+}
+
+NetworkPerformanceServer::NetworkPerformanceServer( AdaptorInternalServices& adaptorServices,
+ const EnvironmentOptions& logOptions )
+: mTriggerEventFactory( adaptorServices.GetTriggerEventFactoryInterface() ),
+ mSocketFactory( adaptorServices.GetSocketFactoryInterface() ),
+ mLogOptions( logOptions ),
+ mServerThread( NULL ),
+ mListeningSocket( NULL ),
+ mClientUniqueId( 0 ),
+ mClientCount( 0 ),
+ mLogFunctionInstalled( false )
+{
+}
+
+NetworkPerformanceServer::~NetworkPerformanceServer()
+{
+ Stop();
+
+ if( mLogFunctionInstalled )
+ {
+ mLogOptions.UnInstallLogFunction();
+ }
+}
+
+void NetworkPerformanceServer::Start()
+{
+ // start a thread to listen for incoming connection requests
+ if (! mServerThread )
+ {
+ if( mListeningSocket )
+ {
+ mSocketFactory.DestroySocket( mListeningSocket );
+ }
+ mListeningSocket = mSocketFactory.NewSocket( SocketInterface::TCP);
+ mListeningSocket->ReuseAddress( true );
+
+ bool bound = false;
+ unsigned int basePort = 0;
+
+ // try a small range of ports, so if multiple Dali apps are running you can select
+ // which one to connect to
+ while( !bound && ( basePort < MAXIMUM_PORTS_TO_TRY ))
+ {
+ bound = mListeningSocket->Bind( SERVER_PORT + basePort );
+ if( !bound )
+ {
+ basePort++;
+ }
+ }
+ if(!bound )
+ {
+ DALI_LOG_ERROR("Failed to bind to a port \n");
+ return;
+ }
+
+ mListeningSocket->Listen( CONNECTION_BACKLOG );
+
+ // start a thread which will block waiting for new connections
+ mServerThread = new boost::thread(boost::bind(&NetworkPerformanceServer::ConnectionListener, this));
+
+ Dali::Integration::Log::LogMessage(Integration::Log::DebugInfo, "~~~ NetworkPerformanceServer started on port %d ~~~ \n", SERVER_PORT + basePort);
+
+ }
+}
+void NetworkPerformanceServer::Stop()
+{
+ if( !mServerThread )
+ {
+ return;
+ }
+ // close the server thread to prevent any new connections
+ mListeningSocket->ExitSelect();
+
+ // wait for the thread to exit.
+ mServerThread->join();
+
+ // close the socket
+ mListeningSocket->CloseSocket();
+
+ delete mServerThread;
+ mSocketFactory.DestroySocket( mListeningSocket );
+
+ mServerThread = NULL;
+ mListeningSocket = NULL;
+
+ // this will tell all client threads to quit
+ StopClients();
+
+ // wait for all threads to exit and the client count to hit zero
+ {
+ boost::mutex::scoped_lock lock(mClientListMutex);
+
+ while (mClientCount != 0)
+ {
+ mClientCountUpdated.wait(lock);
+ }
+ }
+}
+
+bool NetworkPerformanceServer::IsRunning() const
+{
+ if (mServerThread )
+ {
+ return true;
+ }
+ return false;
+}
+
+void NetworkPerformanceServer::ClientThread( NetworkPerformanceClient* client )
+{
+ mClientCount++;
+
+ SocketInterface& socket( client->GetSocket() );
+
+ for( ;; )
+ {
+ SocketInterface::SelectReturn ret = socket.Select();
+
+ if( ret == SocketInterface::DATA_AVAILABLE )
+ {
+ // Read
+ char buffer[ SOCKET_READ_BUFFER_SIZE ];
+ unsigned int bytesRead;
+
+ bool ok = socket.Read( buffer, sizeof( buffer ) , bytesRead);
+ if( ok && ( bytesRead > 0) )
+ {
+ client->ProcessCommand( buffer, bytesRead );
+ }
+ else // if bytesRead == 0, then client closed connection, if ok == false then an error
+ {
+ DeleteClient( client );
+ return;
+ }
+ }
+ else // ret == QUIT or ERROR
+ {
+ DeleteClient( client);
+ return;
+ }
+ }
+}
+
+void NetworkPerformanceServer::ConnectionListener()
+{
+ // install Dali logging function for this thread
+ if( !mLogFunctionInstalled )
+ {
+ mLogOptions.InstallLogFunction();
+ mLogFunctionInstalled = true;
+ }
+
+ for( ;; )
+ {
+ // this will block, waiting for a client to connect
+ // or for mListeningSocket->ExitSelect() to be called
+
+ SocketInterface::SelectReturn ret = mListeningSocket->Select();
+
+ if( ret == SocketInterface::DATA_AVAILABLE )
+ {
+ SocketInterface* clientSocket = mListeningSocket->Accept();
+
+ // new connection made, spawn a thread to handle it
+ NetworkPerformanceClient* client = AddClient( clientSocket );
+ new boost::thread(boost::bind(&NetworkPerformanceServer::ClientThread, this, client));
+ }
+ else // ret == SocketInterface::QUIT or SocketInterface::ERROR
+ {
+ return;
+ }
+ }
+}
+
+NetworkPerformanceClient* NetworkPerformanceServer::AddClient( SocketInterface* clientSocket )
+{
+ // This function is only called from the listening thread
+ NetworkPerformanceClient* client= new NetworkPerformanceClient(clientSocket,
+ mClientUniqueId++,
+ mTriggerEventFactory,
+ *this,
+ mSocketFactory);
+
+ // protect the mClients list which can be accessed from multiple threads.
+ boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
+
+ mClients.PushBack( client );
+
+ return client;
+}
+
+void NetworkPerformanceServer::DeleteClient( NetworkPerformanceClient* client )
+{
+ // protect the mClients list while modifying
+ boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
+
+ // remove from the list, and delete it
+ for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
+ {
+ if( (*iter) == client )
+ {
+ mClients.Erase( iter );
+ delete client;
+
+ // if there server is shutting down, it waits for client count to hit zero
+ mClientCount--;
+
+ // lets the server know the client count has been modified
+ mClientCountUpdated.notify_one();
+ return;
+ }
+ }
+}
+
+void NetworkPerformanceServer::SendData( const char* const data, unsigned int bufferSizeInBytes,unsigned int clientId )
+{
+ if( ! mClientCount )
+ {
+ return;
+ }
+
+ // prevent clients been added / deleted while transmiting data
+ boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
+
+ for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
+ {
+ NetworkPerformanceClient* client = (*iter);
+ if( client->GetId() == clientId )
+ {
+ client->WriteSocket(data ,bufferSizeInBytes);
+ return;
+ }
+ }
+}
+
+void NetworkPerformanceServer::TransmitMarker( const PerformanceMarker& marker, const char* const description )
+{
+ if( ! IsRunning() )
+ {
+ return;
+ }
+ // prevent clients been added / deleted while transmiting data
+ boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
+
+ for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
+ {
+ NetworkPerformanceClient* client = (*iter);
+ client->TransmitMarker( marker, description );
+ }
+}
+
+
+void NetworkPerformanceServer::StopClients()
+{
+ // prevent clients been added / deleted while stopping all clients
+ boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
+
+ for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
+ {
+ NetworkPerformanceClient* client = (*iter);
+ // stop the client from waiting for new commands, and exit from it's thread
+ client->ExitSelect();
+ }
+}
+
+} // namespace Internal
+
+} // namespace Adaptor
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_SERVER_H__
+#define __DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_SERVER_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+#include <dali/public-api/common/dali-vector.h>
+
+// INTERNAL INCLUDES
+#include <base/environment-options.h>
+#include <base/performance-logging/networking/network-performance-client.h>
+#include <base/interfaces/adaptor-internal-services.h>
+
+
+namespace boost
+{
+class thread;
+}
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+class SocketInterface;
+class PerformanceMarker;
+
+/**
+ * @brief The class listens for incoming connections on a dedicated thread.
+ *
+ * When a new connection is established a client thread is spawned to
+ * handle that connection, along with a NetworkPerformanceClient object.
+ * The NetworkPerformanceClient object performs processing of incoming
+ * commands and holds the per-client state information for performance monitoring.
+ *
+ * Server->Start()
+ * - Open socket
+ * - Spawns a thread to listen for incoming connections
+ * <---- New connection
+ * - Spawns a client thread to communicate with new client
+ *
+ * Server->Stop()
+ * - Stops listening thread
+ * - Stops all client threads
+ */
+class NetworkPerformanceServer : public ClientSendDataInterface
+{
+
+public:
+
+ /**
+ * @brief Constructor
+ * @param[in] adaptorServices adaptor internal services
+ * @param[in] logOptions log options
+ */
+ NetworkPerformanceServer( AdaptorInternalServices& adaptorServices, const EnvironmentOptions& logOptions );
+
+
+ /**
+ * @brief Start the server, to be called form Dali main thread
+ * @pre Can only be called form Dali main thread
+ */
+ void Start();
+
+ /**
+ * @brief Stop the server
+ * @pre Can only be called form Dali main thread
+ */
+ void Stop();
+
+ /**
+ * @return true if the server is running
+ */
+ bool IsRunning() const;
+
+ /**
+ * @brief Transmit a marker to any clients are listening for this marker.
+ * @param[in] marker performance marker
+ * @param[in] description marker description
+ * @pre Can be called from any thread
+ *
+ */
+ void TransmitMarker( const PerformanceMarker& marker, const char* const description );
+
+ /**
+ * Destructor
+ */
+ ~NetworkPerformanceServer();
+
+protected: // ClientSendDataInterface
+
+ /**
+ * @copydoc ClientSendDataInterface::ClientSendDataInterface()
+ */
+ virtual void SendData( const char* const data, unsigned int bufferSizeInBytes, unsigned int clientId );
+
+private:
+ /**
+ * @brief Client thread function
+ * @param client network client object
+ */
+ void ClientThread( NetworkPerformanceClient* client );
+
+ /**
+ * @brief Stop all client threads
+ */
+ void StopClients();
+
+ /**
+ * @brief Waits for new connections to be made
+ */
+ void ConnectionListener();
+
+ /**
+ * @brief Add a new client to the client list
+ * @param clientSocket client socket
+ * @return client
+ */
+ NetworkPerformanceClient* AddClient( SocketInterface* clientSocket );
+
+ /**
+ * @brief Delete a client from the client list
+ * @param client network client
+ */
+ void DeleteClient( NetworkPerformanceClient* client );
+
+ NetworkPerformanceServer( const NetworkPerformanceServer& ); ///< undefined copy constructor
+ NetworkPerformanceServer& operator=( const NetworkPerformanceServer& ); ///< undefined assignment operator
+
+
+ TriggerEventFactoryInterface& mTriggerEventFactory; ///< used to create trigger events
+ SocketFactoryInterface& mSocketFactory; ///< used to create sockets
+ const EnvironmentOptions& mLogOptions; ///< log options
+ Dali::Vector< NetworkPerformanceClient* > mClients; ///< list of connected clients
+ boost::thread* mServerThread; ///< thread that listens for new connections
+ SocketInterface* mListeningSocket; ///< socket used to listen for new connections
+ boost::mutex mClientListMutex; ///< mutex
+ boost::condition_variable mClientCountUpdated; ///< used to say the client count has changed
+ unsigned int mClientUniqueId; ///< increments for every client connection
+ unsigned int mClientCount; ///< client count
+ bool mLogFunctionInstalled; ///< whether the log function is installed
+
+};
+
+
+} // namespace Internal
+
+} // namespace Adaptor
+
+} // namespace Dali
+
+#endif //__DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_SERVER_H__
:mPlatformAbstraction( adaptorServices.GetPlatformAbstractionInterface() ),
mEnvironmentOptions( environmentOptions ),
mKernelTrace( adaptorServices.GetKernelTraceInterface() ),
+ mNetworkServer( adaptorServices, environmentOptions ),
mStatContextManager( *this ),
mStatisticsLogBitmask( 0 ),
+ mNetworkControlEnabled( mEnvironmentOptions.GetNetworkControlMode()),
mLoggingEnabled( false ),
mLogFunctionInstalled( false )
{
SetLogging( mEnvironmentOptions.GetPerformanceStatsLoggingOptions(),
mEnvironmentOptions.GetPerformanceTimeStampOutput(),
mEnvironmentOptions.GetPerformanceStatsLoggingFrequency());
+
+ if( mNetworkControlEnabled )
+ {
+ mLoggingEnabled = true;
+ mNetworkServer.Start();
+ }
}
PerformanceServer::~PerformanceServer()
{
+ if( mNetworkControlEnabled )
+ {
+ mNetworkServer.Stop();
+ }
+
if( mLogFunctionInstalled )
{
mEnvironmentOptions.UnInstallLogFunction();
unsigned int timeStampOutput,
unsigned int logFrequency )
{
- if( ( statisticsLogOptions == 0) && ( timeStampOutput == 0 ))
- {
- mLoggingEnabled = false;
- return;
- }
-
mStatisticsLogBitmask = statisticsLogOptions;
mPerformanceOutputBitmask = timeStampOutput;
mStatContextManager.SetLoggingLevel( mStatisticsLogBitmask, logFrequency);
- mLoggingEnabled = true;
+ if( ( mStatisticsLogBitmask == 0) && ( mPerformanceOutputBitmask == 0 ))
+ {
+ mLoggingEnabled = false;
+ }
+ else
+ {
+ mLoggingEnabled = true;
+ }
}
void PerformanceServer::SetLoggingFrequency( unsigned int logFrequency, ContextId contextId )
void PerformanceServer::LogMarker( const PerformanceMarker& marker, const char* const description )
{
-
-
+ // log to the network ( this is thread safe)
+ if( mNetworkControlEnabled )
+ {
+ mNetworkServer.TransmitMarker( marker, description );
+ }
// log to kernel trace
if( mPerformanceOutputBitmask & OUTPUT_KERNEL_TRACE )
*
*/
-// EXTERNAL INCLUDES
-#include <boost/thread/mutex.hpp>
+// EXTERNAL INCLDUES
#include <dali/public-api/common/dali-vector.h>
// INTERNAL INCLUDES
#include <base/performance-logging/frame-time-stats.h>
+#include <base/performance-logging/networking/network-performance-server.h>
#include <base/interfaces/adaptor-internal-services.h>
#include <base/performance-logging/performance-marker.h>
#include <base/performance-logging/statistics/stat-context-manager.h>
const EnvironmentOptions& mEnvironmentOptions; ///< environment options
KernelTraceInterface& mKernelTrace; ///< kernel trace interface
boost::mutex mDataMutex; ///< mutex
+ NetworkPerformanceServer mNetworkServer; ///< network server
StatContextManager mStatContextManager; ///< Stat context manager
unsigned int mStatisticsLogBitmask; ///< statistics log level
unsigned int mPerformanceOutputBitmask; ///< performance marker output
+ bool mNetworkControlEnabled:1; ///< Whether network control is enabled
bool mLoggingEnabled:1; ///< whether logging update / render to a log is enabled
bool mLogFunctionInstalled:1; ///< whether the log function is installed
};
unsigned int logPerformanceStats = GetIntegerEnvironmentVariable( DALI_ENV_LOG_PERFORMANCE_STATS, 0 );
unsigned int logPerformanceStatsFrequency = GetIntegerEnvironmentVariable( DALI_ENV_LOG_PERFORMANCE_STATS_FREQUENCY, 0 );
unsigned int performanceTimeStampOutput= GetIntegerEnvironmentVariable( DALI_ENV_PERFORMANCE_TIMESTAMP_OUTPUT, 0 );
-
+ unsigned int networkControl= GetIntegerEnvironmentVariable( DALI_ENV_NETWORK_CONTROL, 0 );
unsigned int logPanGesture = GetIntegerEnvironmentVariable( DALI_ENV_LOG_PAN_GESTURE, 0 );
// all threads here (event, update, and render) will send their logs to TIZEN Platform's LogMessage handler.
Dali::Integration::Log::LogFunction logFunction(Dali::TizenPlatform::LogMessage);
- mEnvironmentOptions.SetLogOptions( logFunction, logFrameRateFrequency, logupdateStatusFrequency, logPerformanceStats, logPerformanceStatsFrequency, performanceTimeStampOutput, logPanGesture );
+ mEnvironmentOptions.SetLogOptions( logFunction, networkControl, logFrameRateFrequency, logupdateStatusFrequency, logPerformanceStats, logPerformanceStatsFrequency, performanceTimeStampOutput, logPanGesture );
int predictionMode;
if( GetIntegerEnvironmentVariable(DALI_ENV_PAN_PREDICTION_MODE, predictionMode) )
{
return mTriggerEventFactory;
}
+
+SocketFactoryInterface& Adaptor::GetSocketFactoryInterface()
+{
+ return mSocketFactory;
+}
+
RenderSurface* Adaptor::GetRenderSurfaceInterface()
{
return mSurface;
#include <window-visibility-observer.h>
#include <kernel-trace.h>
#include <trigger-event-factory.h>
+#include <networking/socket-factory.h>
namespace Dali
{
virtual TriggerEventFactoryInterface& GetTriggerEventFactoryInterface();
/**
+ * @copydoc Dali::Internal::Adaptor::AdaptorInternalServices::GetSocketFactoryInterface()
+ */
+ virtual SocketFactoryInterface& GetSocketFactoryInterface();
+
+ /**
* @copydoc Dali::Internal::Adaptor::AdaptorInternalServices::GetRenderSurfaceInterface()
*/
virtual RenderSurface* GetRenderSurfaceInterface();
KernelTrace mKernelTracer; ///< Kernel tracer
TriggerEventFactory mTriggerEventFactory; ///< Trigger event factory
ObjectProfiler* mObjectProfiler; ///< Tracks object lifetime for profiling
+ SocketFactory mSocketFactory; ///< Socket factory
public:
inline static Adaptor& GetImplementation(Dali::Adaptor& adaptor) {return *adaptor.mImpl;}
};
$(adaptor_common_dir)/events/pinch-gesture-detector.cpp \
$(adaptor_common_dir)/events/tap-gesture-detector.cpp \
\
+ $(adaptor_common_dir)/networking/socket-impl.cpp \
+ $(adaptor_common_dir)/networking/socket-factory.cpp \
+ \
$(adaptor_common_dir)/feedback/feedback-controller.cpp \
$(adaptor_common_dir)/feedback/feedback-plugin-proxy.cpp \
\
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+// CLASS HEADER
+#include "socket-factory.h"
+
+// INTERNAL INCLUDES
+#include <networking/socket-impl.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+SocketInterface* SocketFactory::NewSocket( SocketInterface::Protocol protocol )
+{
+ return new Socket( protocol );
+}
+
+void SocketFactory::DestroySocket( SocketInterface* socketInterface )
+{
+ Socket* socket( static_cast<Socket* >( socketInterface ));
+ delete socket;
+}
+
+} // Adaptor
+
+} // Internal
+
+} // Dali
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_SOCKET_FACTORY_H__
+#define __DALI_INTERNAL_ADAPTOR_SOCKET_FACTORY_H__
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <base/interfaces/socket-factory-interface.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+/**
+ * @brief concrete implementation of the socket factory interface
+ */
+class SocketFactory : public SocketFactoryInterface
+{
+public:
+
+
+ /**
+ * @brief Constructor
+ */
+ SocketFactory( )
+ {
+ }
+
+ /**
+ * @brief destructor
+ */
+ virtual ~SocketFactory()
+ {
+ }
+
+ /**
+ * @copydoc SocketFactoryInterface::NewSocket()
+ */
+ virtual SocketInterface* NewSocket( SocketInterface::Protocol protocol );
+
+ /**
+ * @copydoc SocketFactoryInterface::DestroySocket()
+ */
+ virtual void DestroySocket( SocketInterface* socket );
+
+};
+
+} // Adaptor
+
+} // Internal
+
+} // Dali
+
+#endif // __DALI_INTERNAL_ADAPTOR_SOCKET_FACTORY_H__
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "socket-impl.h"
+
+// EXTERNAL INCLUDES
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dali/integration-api/debug.h>
+
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+
+namespace
+{
+const unsigned int MAX_SOCKET_DATA_WRITE_SIZE = 1024 * 1024 * 10 ; // limit maximum size to write to 10 MB
+}
+
+Socket::Socket( Protocol protocol , int fileDescriptor )
+:mSocketFileDescriptor( fileDescriptor ),
+ mBound(false),
+ mListening(false),
+ mQuitPipeCreated(false),
+ mBlocked(false)
+{
+ int addressFamily( AF_INET );
+ int netProtocol( IPPROTO_TCP );
+ int type( SOCK_STREAM ); // for TCP
+
+ if( protocol == UDP )
+ {
+ type = SOCK_DGRAM;
+ netProtocol = IPPROTO_UDP;
+ }
+ if( mSocketFileDescriptor == -1)
+ {
+ mSocketFileDescriptor = socket( addressFamily,type, netProtocol);
+ }
+ else
+ {
+ // socket already open
+ mBound = true;
+ }
+}
+
+Socket::~Socket()
+{
+ if( SocketIsOpen() )
+ {
+ CloseSocket();
+ }
+}
+
+bool Socket::SocketIsOpen() const
+{
+ return (mSocketFileDescriptor != -1);
+}
+
+bool Socket::CloseSocket()
+{
+
+ if( ! SocketIsOpen() )
+ {
+ DALI_LOG_ERROR("Socket already closed or is invalid \n");
+ return false;
+ }
+
+ int ret = close( mSocketFileDescriptor );
+ mSocketFileDescriptor = -1;
+ mListening = false;
+ mBound = false;
+
+ if( ret == -1 )
+ {
+ DALI_LOG_ERROR("Socket close failed");
+ return false;
+ }
+ return true;
+}
+
+bool Socket::Bind( uint16_t port )
+{
+ if( ! SocketIsOpen() || mBound )
+ {
+ DALI_LOG_ERROR("Socket is invalid, or already bound");
+ return false;
+ }
+ struct sockaddr_in serverAddress;
+
+ memset( &serverAddress, 0, sizeof(serverAddress) );
+ serverAddress.sin_family = AF_INET; // internet
+ serverAddress.sin_port = htons( port ); // host-to-net short (16-bit) translation
+ serverAddress.sin_addr.s_addr = htonl( INADDR_ANY ); // binds the socket to all available interfaces
+
+ int ret = bind( mSocketFileDescriptor,
+ (struct sockaddr* ) &serverAddress,
+ sizeof(serverAddress));
+
+ if( ret == -1 )
+ {
+ DALI_LOG_ERROR("bind failed for port %d %s \n", port, strerror(errno) );
+ return false;
+ }
+
+ mBound = true;
+
+ return true;
+}
+
+bool Socket::Listen( int blacklog)
+{
+ if( ! mBound || mListening )
+ {
+ DALI_LOG_ERROR("socket is not bound, or already opened for listening");
+ return false;
+ }
+ int ret = listen( mSocketFileDescriptor, blacklog);
+
+ if( ret == -1 )
+ {
+ DALI_LOG_ERROR("Listen failed");
+ return false;
+ }
+
+ mListening = true;
+
+ return true;
+}
+
+SocketInterface* Socket::Accept() const
+{
+ if( !mListening )
+ {
+ DALI_LOG_ERROR("socket is not being listened to");
+ return NULL;
+ }
+
+ struct sockaddr clientAddress;
+
+ socklen_t addressLength(sizeof(sockaddr_in));
+
+ int clientFileDescriptor = accept( mSocketFileDescriptor, &clientAddress, &addressLength);
+ if( clientFileDescriptor == -1 )
+ {
+ DALI_LOG_ERROR("Accept failed");
+ return NULL;
+ }
+
+ // create a new socket, only TCP supports connections
+ Socket* client = new Socket( TCP, clientFileDescriptor );
+
+ return client;
+}
+
+bool Socket::CreateQuitPipe()
+{
+ if( !mQuitPipeCreated )
+ {
+ // create a pipe file descriptor to be able to break from the Select statement
+ //
+ int ret = pipe( mQuitPipe );
+ if( ret != 0)
+ {
+ DALI_LOG_ERROR("Pipe creation failed");
+ return false;
+ }
+ mQuitPipeCreated = true;
+ }
+ return true;
+}
+void Socket::DeleteQuitPipe()
+{
+ if( mQuitPipeCreated )
+ {
+ close( mQuitPipe[0] );
+ close( mQuitPipe[1] );
+ }
+}
+
+SocketInterface::SelectReturn Socket::Select()
+{
+ bool ok = CreateQuitPipe();
+ if( !ok )
+ {
+ return ERROR;
+ }
+
+ fd_set readFileDescriptors, exceptFileDescriptors;
+ FD_ZERO(&readFileDescriptors);
+ FD_ZERO(&exceptFileDescriptors);
+
+ FD_SET(mSocketFileDescriptor,&readFileDescriptors );
+ FD_SET(mQuitPipe[0],&readFileDescriptors );
+
+ FD_SET(mSocketFileDescriptor,&exceptFileDescriptors);
+
+ unsigned int maxFd = mQuitPipe[0] > mSocketFileDescriptor ? mQuitPipe[0]: mSocketFileDescriptor;
+
+ for( ;; )
+ {
+ // this will block waiting for file descriptors
+ int ret = select( maxFd+1, &readFileDescriptors, NULL, &exceptFileDescriptors, NULL );
+ if( ret == -1 )
+ {
+ DALI_LOG_ERROR("select failed");
+ return ERROR;
+ }
+ else if ( FD_ISSET( mQuitPipe[0] , &readFileDescriptors ))
+ {
+ // ExitSelect() called
+ return QUIT;
+ }
+ else if ( FD_ISSET( mSocketFileDescriptor, &readFileDescriptors ))
+ {
+ // socket data received
+ return DATA_AVAILABLE;
+ }
+ }
+ return QUIT;
+}
+
+void Socket::ExitSelect()
+{
+ if( mQuitPipeCreated )
+ {
+ // write a single character to the pipe (can be anything)
+ char c = ' ';
+ int ret = write( mQuitPipe[1], &c, 1);
+ if( ret < 1 )
+ {
+ DALI_LOG_ERROR("ExitSelect failed!\n");
+ }
+ return;
+ }
+}
+
+bool Socket::ReuseAddress( bool reUse )
+{
+ if( ! SocketIsOpen() | mBound )
+ {
+ DALI_LOG_ERROR("Socket is invalid or already bound \n");
+ return false;
+ }
+
+ int reUseInteger = reUse; // convert it to an int
+
+ int ret = setsockopt( mSocketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &reUseInteger, sizeof(reUseInteger));
+ if( ret == -1 )
+ {
+ DALI_LOG_ERROR("SO_REUSEADDR option failed %s \n",strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool Socket::SetBufferSize( SocketInterface::BufferType type, unsigned int size )
+{
+ if( ! SocketIsOpen() || mBound )
+ {
+ DALI_LOG_ERROR("Socket is invalid or already bound \n");
+ return false;
+ }
+ int option = SO_RCVBUF;
+ if( type == SocketInterface::SEND_BUFFER )
+ {
+ option = SO_SNDBUF;
+ }
+
+ int ret = setsockopt( mSocketFileDescriptor, SOL_SOCKET,option,&size,sizeof(size));
+ if( ret == -1 )
+ {
+ DALI_LOG_ERROR("SO_RCVBUF / SO_SNDBUF option failed \n");
+ return false;
+ }
+ return true;
+}
+
+bool Socket::Read( void* buffer, unsigned int bufferSizeInBytes, unsigned int& bytesRead )
+{
+ bytesRead = 0;
+
+ if( !SocketIsOpen() )
+ {
+ DALI_LOG_ERROR("Socket is invalid \n");
+ return false;
+ }
+
+ bytesRead = read( mSocketFileDescriptor, buffer, bufferSizeInBytes );
+
+ return true;
+}
+
+bool Socket::Write( const void* buffer, unsigned int bufferSizeInBytes )
+{
+ if( !SocketIsOpen() )
+ {
+ DALI_LOG_ERROR("Socket is invalid \n");
+ return false;
+ }
+
+ // check we don't try to write more than 10MB ( this can be increased if required)
+ if( bufferSizeInBytes > MAX_SOCKET_DATA_WRITE_SIZE )
+ {
+ DALI_LOG_ERROR("Writing %d bytes exceeds MAX_SOCKET_DATA_WRITE_SIZE of %d bytes \n", bufferSizeInBytes, MAX_SOCKET_DATA_WRITE_SIZE);
+ return false;
+ }
+
+ int bytesWritten = 0;
+
+ // write isn't guaranteed to write the entire buffer in one go
+
+ while( bytesWritten != static_cast< int>(bufferSizeInBytes))
+ {
+ const char* byteBuffer = static_cast<const char *>( buffer );
+ byteBuffer+=bytesWritten;
+
+ int ret = write( mSocketFileDescriptor, byteBuffer, bufferSizeInBytes - bytesWritten );
+ if( ret < 1)
+ {
+ DALI_LOG_ERROR("Socket writer error \n");
+ return false;
+ }
+ else
+ {
+ bytesWritten += ret;
+ }
+ }
+ return true;
+}
+
+} // Adaptor
+} // Internal
+} // Dali
+
--- /dev/null
+#ifndef __DALI_INTERNAL_ADAPTOR_SOCKET_IMPL_H__
+#define __DALI_INTERNAL_ADAPTOR_SOCKET_IMPL_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <base/interfaces/socket-interface.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+/**
+ * @brief Concrete implementation of a socket under Linux.
+ *
+ * Provides automatic closing of socket on destruction.
+ */
+class Socket : public SocketInterface
+{
+public:
+
+ /**
+ * @brief Constructor
+ * @param protocol network protocol
+ * @param fileDescriptor option file descriptor if the socket is already open
+ */
+ Socket( Protocol protocol , int fileDescriptor = -1 );
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketIsOpen()
+ */
+ virtual bool SocketIsOpen() const;
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::CloseSocket
+ */
+ virtual bool CloseSocket();
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::Bind
+ */
+ virtual bool Bind( uint16_t port ) ;
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::Listen
+ */
+ virtual bool Listen( int blacklog);
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::Accept
+ */
+ virtual SocketInterface* Accept() const ;
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::Select
+ */
+ virtual SelectReturn Select( );
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::ExitSelect
+ */
+ virtual void ExitSelect();
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::Recieve
+ */
+ virtual bool Read( void* buffer, unsigned int bufferSizeInBytes, unsigned int& bytesRead );
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::Send
+ */
+ virtual bool Write( const void* buffer, unsigned int bufferLength );
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::ReuseAddress
+ */
+ virtual bool ReuseAddress( bool reUse );
+
+ /**
+ * @copydoc Dali::Internal::Adaptor::SocketInterface::SetBufferSize
+ *
+ */
+ virtual bool SetBufferSize( SocketInterface::BufferType type, unsigned int bufferSizeInBytes );
+
+ /**
+ * @brief Virtual destructor
+ */
+ virtual ~Socket();
+
+private:
+
+
+ /**
+ * @brief Helper to create the quit pipe
+ */
+ bool CreateQuitPipe();
+
+ /**
+ * @brief Helper to delete the quit pipe
+ */
+ void DeleteQuitPipe();
+
+ int mSocketFileDescriptor; ///< file descriptor
+ int mQuitPipe[2]; ///< Pipe to inform Select to quit.
+ bool mBound:1; ///< whether the socket is bound
+ bool mListening:1; ///< whether the socket is being listen to
+ bool mQuitPipeCreated:1; ///< whether the quit pipe has been created
+ bool mBlocked:1; ///< whether the socket is blocked waiting for a connection
+};
+
+
+
+} // Adaptor
+
+} // Internal
+
+} // Dali
+
+#endif // __DALI_INTERNAL_ADAPTOR_SOCKET_IMPL_H__