hellocorelua

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

hellocorelua [2014/03/22 21:45]
mroriz [Execution]
hellocorelua [2017/07/21 03:08]
Line 1: Line 1:
-====== HelloCoreLua ====== 
-{{ :hellocorelua.png?nolink&400|}} 
-This tutorial will guide you through the basic concepts and programming involved in the communication between a mobile node (MN) in [[wp>Lua]] and a stationary node within the SDDL core network [[architecture]]. The tutorial focus will be on the Lua client, precisely explaining how to connect, send, and receive messages to and from a SDDL core. Like in the HelloCore tutorial in Java, the stationary node of the SDDL core will play the role of a server processing node, capable of processing application messages from the Lua mobile node (MN). 
- 
-One of the key differences between programming in Java and Lua for SDDL is the need to define the exchanged messages in a language-independent format, such as XDR, JSON, Google Protocol Buffers, etc. In this sample, we opted for JSON to express the messages changed between the client and server. Note that both instances - client and server - need to serialize their message in this format. 
- 
-Our sample application is composed of two components: a lua mobile client node and a stationary java node, as illustrated in the figure on the right. The MN creates a message, containing a random latitude, longitude and a custom id (expressed in uuid), which is sent to the SDDL core. Our processing node receives this message and execute a simple logic, keeps track on the number of messages received and prints them on the screen. To exemplify the other path of communication, the processing node sends a reply message to the MN containing the number of message that it received so far.  
- 
-The tutorial will focus on the code of the lua mobile client application, and then continues with the discussion of the adaptation of the processing node's code. As a matter of simplification, the entire application system, composed of the mobile client, gateway, and processing node,  runs locally on a single machine, but it can be easily modified to run on distributed machines. 
- 
-<WRAP tip> 
-You do not need to copy the source code snippets shown in this tutorial. At the end of each section there is a link to download the entire  source code of the section. Moreover, all examples are included in the Lua clientlib client distribution. 
-</WRAP> 
- 
-===== LuaMobileClient ===== 
-To create our Lua mobile client application, create a Lua program called ''helloclient.lua''. First of all, you will need to import the ClientLib libraries, which are the following: 
- 
-<code lua> 
--- clientlib requires 
-local logging    = require("logging") 
-local dispatcher = require("lac.dispatcher") 
-local node       = require("lac.clientlib.mrudp.node") 
-local messages   = require("lac.clientlib.messages") 
-</code>  
- 
-In addition to these libraries, we will also import a set of Lua libraries to send messages in a language-independent format. For this sample, we choose JSON to describe the exchanged data because it is very flexible and has a direct mapping to Lua and Java. Note that the developer is free to choose any language-independent format to exchange messages between the mobile client and the processing node. The only requirement is that the both need to be able to understand the exchanged bytes. 
- 
-<code Lua> 
--- app specific requires 
-local math       = require("math") 
-local uuid       = require("uuid") 
-local json       = require("dkjson") 
-</code> 
- 
-In SDDL, nodes that are outside the core (either mobile or stationary) interact directly with the Gateway rather than the processing nodes, since it uses a different protocol ([[ClientLib]] and [[MRUDP]]) than the core ([[wp>Data_Distribution_Service|Data Distribution Service (DDS)]]). The Gateway translate the node message to the core protocol and vice-versa. Thus, the primary interaction of the client is with the gateway. Therefore, we first declare two variables to locate our gateway, its IP address and port number. Since in this tutorial we are running our entire application (mobile client node, gateway, and processing node) locally, we can specify the gateway IP to the loopback address ''127.0.0.1'' and choose its default port ''5500''. In a real distributed scenario, the client would change these variables to the gateways IP and port respectively.  
- 
-<code lua> 
--- server info 
-local gw_ip    = "127.0.0.1" 
-local gw_port  = 5500 
-</code> 
- 
-After that, we specify our logging information. The logger shows ClientLib debug information; it is very useful if something goes wrong. Since we are doing a basic application, we will instruct the logger to show only messages that are equal or above ''FATAL'' level (more on logging level [[http://www.keplerproject.org/lualogging/ |here]]). The dispatcher act as a "thread" manager in Lua. It uses coroutines to switch context between sending, receiving, and other internal function tasks. We only need to create a dispatcher, this entire logic is hidden from the developer. 
- 
-<code lua> 
--- logger 
-logging.console = require("logging.console") 
- 
--- dispatcher 
-local disp      = dispatcher.new() 
-</code> 
- 
-After that, we specify a listener for our Lua MN client. The ClientLib Lua listener is a table that contains attributes that are functions. These functions are triggered by the library when an event happens, such as when the client receives a message, connects, disconnects or reconnects into a gateway. They receive the MN connection as argument and the message received or unsent. We will focus on the ''new_message_received'' function. Our function prints in the screen the received message and the size of it (in bytes). We expected that the server sended a message in a JSON format so we decode the message bytes into a JSON table. Finally, our receive message function prints the key and values of the JSON table that we decoded. 
- 
-Note that, we can also prefix the listeners functions arguments with a custom object, in this case we prefix with a ''prefix_variable''. Prefix variables can be specified when we attach the listener to the connection, as we will explain later in the tutorial. By doing that, in every function call of our listener the middleware will pass the prefix argument along with the default arguments (''node'' and ''msg''). This is useful if you want to access a custom variable inside the listener functions. The code for the listener is displayed below: 
- 
-<code lua> 
--- listener 
-local listener = { 
-  new_message_received = function(prefix_variable, node, msg) 
-    print("Message Received!", node, msg) 
-    print("got a JSON object with ".. #msg.content .." bytes") 
-  
- local json_content, pos, err = json.decode (msg.content, 1, nil)  
-    for k, v in pairs(json_content) do 
-      print(k, v) 
-    end 
-  end, 
-   
-  unsent_messages = function(prefix_variable, node, msgs) 
-    print("Unsent messages" .. #msgs)   
-  end, 
-   
-  connected = function(node) 
-    print("Connected") 
-  end, 
- 
-  reconnected = function(node) 
-    print("Reconnected") 
-  end, 
- 
-  disconnected = function(node) 
-    print("Disconnected") 
-  end, 
-} 
-</code> 
- 
-Our client will send a table containing a random latitude, longitude and UUID. The following data displays this table: 
- 
-<code Lua> 
--- content 
-local client_data = { 
-  lat  = -1 * math.random(22, 25), 
-  lng  = -1 * math.random(31, 33), 
-  uuid = uuid.new() 
-} 
-</code> 
- 
-The data will be sent repeatedly by our client application to the Java server, which is the main logic of our program. We will specify the entry routine of our Lua mobile client in a function called ''main''. This function will be passed as argument to the dispatcher, which in turn start the execution of the routine.  
- 
-First we instantiate the logging feature and the minimum logging level to ''FATAL''. After that, we start the connection between the client and a gateway. The client connection is very straightforward; first, we create a connection using the ''node.new()'' function. The first argument is a scheduler (dispatcher), the second is an existing socket, the third is the sender UUID, the fourth are the connection configuration parameters,  and the last is the logger table. The function definition is: 
- 
-  node.new(scheduler, skt, sender_uuid, cfg, logger) 
- 
-The connection table, encapsulate the details and protocol involved in the physical connection with the gateway, for instance, it uses unique ids to identify the client and the connection when the node changes IPs. If we do not specify an argument in the connection instantiation, it automatically generates an UUID to identify the client. As you have read in the basic concepts, this id is used to unique identify the client independent of his IP, which is useful for handover and abstracting the client location. 
- 
-After creating the node connection, we establish the connection calling the ''connect()'' method passing the gateway address and port as parameter. Finally, we attach our listener in the connection, this listener holds methods related to the node gateway connection. It will trigger functions to indicate that the node has successfully connected, disconnected, reconnected from the gateway alongside with operations to receive new messages, and unsent messages (messages that were timeouted and not sent due to disconnection). 
- 
-The routine has a main loop that executes forever. We attribute random values to our client_data content. This content is encoded into a JSON so that we can communicate with the Java server. We create an application message, using the ''message.new()'' function. Like in the Java client, we set the application message content using the ''set_content_lua_obj()'' and specify that the content is encoded in JSON. The other formats available are: Java, Protocol Buffers and XML format.  We send the message using the ''send_message(appmsg)'' in the connection table. Finally, we attach the main routine to the dispatcher. 
- 
-The entire code of our lua client is displayed below: 
-<file Lua helloclient.lua> 
---[[ 
-    Hello Core Lua Client 
-    Simple lua client that sends every 10s a table containig a random lat, lng and a custom ID (uuid). 
---]] 
- 
--- clientlib requires 
-local logging    = require("logging") 
-local dispatcher = require("lac.dispatcher") 
-local node       = require("lac.clientlib.mrudp.node") 
-local messages   = require("lac.clientlib.messages") 
- 
--- app specific requires 
-local math       = require("math") 
-local uuid       = require("uuid") 
-local json       = require("dkjson") 
- 
--- server info 
-local gw_ip    = "127.0.0.1" 
-local gw_port  = 5500 
- 
--- logger 
-logging.console = require("logging.console") 
- 
--- dispatcher 
-local disp     = dispatcher.new() 
- 
--- listener 
-local listener = { 
-  new_message_received = function(prefix_variable, node, msg) 
-    print("Message Received!", node, msg) 
-    print("got a JSON object with ".. #msg.content .." bytes") 
-  
- local json_content, pos, err = json.decode (msg.content, 1, nil)  
-    for k, v in pairs(json_content) do 
-      print(k, v) 
-    end 
-  end, 
-   
-  unsent_messages = function(prefix_variable, node, msgs) 
-    print("Unsent messages" .. #msgs)   
-  end, 
-   
-  connected = function(prefix_variable, node) 
-    print("Connected") 
-  end, 
- 
-  reconnected = function(prefix_variable, node) 
-    print("Reconnected") 
-  end, 
- 
-  disconnected = function(prefix_variable, node) 
-    print("Disconnected") 
-  end, 
-} 
- 
--- content 
-local client_data = { 
-  lat  = -1 * math.random(22, 25), 
-  lng  = -1 * math.random(31, 33), 
-  uuid = uuid.new() 
-} 
- 
-function main() 
-  local logger = logging.console() 
-  logger:setLevel(logging.FATAL) 
-  
- 
-  local connection = node.new(disp, nil, client_data.uuid, nil, logger) 
-  connection:connect(gw_ip, gw_port) 
-   
-  -- we could pass a prefix variable, e.g.: 
-  -- add_listener("external", listener, prefix_variable, prefix_variable2, ...) 
-  connection:add_listener("external", listener)  
-   
- while true do 
-    -- random lat and lng and encode in json 
-    client_data.lat = -1 * math.random(22, 25) 
-    client_data.lng = -1 * math.random(31, 33) 
-    local lua_content_in_json = json.encode(client_data, {indent = true}) 
- 
-    local appMSG = messages.new() 
-    appMSG.content_type = messages.content_types.JSON 
-    appMSG:set_content_lua_obj(lua_content_in_json) 
-    connection:send_message(appMSG) 
- 
-    disp:sleep(20) 
-  end 
- 
-  disp:sleep(1) 
-  connection:disconnect() 
- 
-  disp:sleep(1) 
-end 
- 
-disp:start(main) 
-</file> 
-==== Processing Node ==== 
-The core nodes establishes premises and protocols that are not suitable for mobile applications. To expand this processing power to mobile nodes, we added the concept of gateways that use our lightweight protocol to communicate with mobile nodes and translate their message to core messages. Developers can instantiate nodes to process the mobile nodes message.   
- 
-To create our processing node application, create an empty Java ''HelloCoreServer'' Class using the ''br.pucrio.inf.lac.helloworld'' package. You can create this class in the existing ''HelloCore'' project or create a separated project, for now we will make the class in the existing project. The processing nodes communicate using [[SDDL]], an abstraction that encapsulate the [[wp>DDS|DDS]] communication.  
- 
-The main difference between the Processing Node of this version and the [[hellocore|Java Hello Core]] Server is the addition of the transformation of the response to JSON. We will send the number of messages and an attribute country to the client. The entire code of our server is displayed below. The difference exists on the ''onNewData'' method. 
- 
-<file java HelloCoreServer.java> 
-package br.pucrio.inf.lac.helloworld; 
- 
-import java.util.logging.Level; 
-import java.util.logging.Logger; 
- 
-import org.json.simple.JSONObject; 
- 
-import lac.cnclib.sddl.message.ApplicationMessage; 
-import lac.cnclib.sddl.serialization.Serialization; 
-import lac.cnet.sddl.objects.ApplicationObject; 
-import lac.cnet.sddl.objects.Message; 
-import lac.cnet.sddl.objects.PrivateMessage; 
-import lac.cnet.sddl.udi.core.SddlLayer; 
-import lac.cnet.sddl.udi.core.UniversalDDSLayerFactory; 
-import lac.cnet.sddl.udi.core.listener.UDIDataReaderListener; 
- 
-public class HelloCoreServer implements UDIDataReaderListener<ApplicationObject> { 
-  SddlLayer  core; 
-  int        counter; 
- 
-  public static void main(String[] args) { 
-    Logger.getLogger("").setLevel(Level.OFF); 
- 
-    new HelloCoreServer(); 
-  } 
- 
-  public HelloCoreServer() { 
-    core = UniversalDDSLayerFactory.getInstance(); 
-    core.createParticipant(UniversalDDSLayerFactory.CNET_DOMAIN); 
- 
-    core.createPublisher(); 
-    core.createSubscriber(); 
- 
-    Object receiveMessageTopic = core.createTopic(Message.class, Message.class.getSimpleName()); 
-    core.createDataReader(this, receiveMessageTopic); 
- 
-    Object toMobileNodeTopic = core.createTopic(PrivateMessage.class, PrivateMessage.class.getSimpleName()); 
-    core.createDataWriter(toMobileNodeTopic); 
- 
-    counter = 0; 
-    synchronized (this) { 
-      try { 
-        this.wait(); 
-      } catch (InterruptedException e) { 
-        e.printStackTrace(); 
-      } 
-    } 
-  } 
- 
-  @Override 
-  public void onNewData(ApplicationObject topicSample) { 
-    Message message = (Message) topicSample; 
-    System.out.println(Serialization.fromJavaByteStream(message.getContent())); 
- 
-    PrivateMessage privateMessage = new PrivateMessage(); 
-    privateMessage.setGatewayId(message.getGatewayId()); 
-    privateMessage.setNodeId(message.getSenderId()); 
- 
-    synchronized (core) { 
-       JSONObject jsonObject      = new JSONObject(); 
-       jsonObject.put("counter", counter); 
-       jsonObject.put("country", "Brazil"); 
-    } 
-         
-    String content = jsonObject.toJSONString(); 
-    ApplicationMessage appMsg = new ApplicationMessage(); 
-    appMsg.setContentObject(content); 
-     
-    privateMessage.setMessage(Serialization.toProtocolMessage(appMsg)); 
-    core.writeTopic(PrivateMessage.class.getSimpleName(), privateMessage); 
-  } 
- 
-} 
-</file> 
- 
-==== Execution ==== 
- 
-To execute our application, we need to first create the gateway, which will instantiate the core infrastructure if none exists. The gateway receives two parameters, its public IP and a given port number. Since we are this sample locally, we can choose the loopback IP ''127.0.0.1'' and the default middleware port ''5500''. To do that, open a shell, and run the following command: 
- 
-  $ gateway 127.0.0.1 5500 
- 
-After that, we instantiate our processing node. To do that, run the ''HelloCoreServer'' class in Eclipse as a Java application. Finally, execute the mobile client running the lua command line interpreter:  
- 
-  $ lua helloclient.lua 
- 
  
  • hellocorelua.txt
  • Last modified: 2017/07/21 03:08
  • (external edit)