Difference between revisions of "Generic Serial Device"

From LinuxMCE
Jump to: navigation, search
m (spelling corrections)
Line 1: Line 1:
 
<table width="100%"> <tr><td bgcolor="#FFCFCF">This page was written by Pluto and imported with their permission when LinuxMCE branched off in February, 2007.  In general any information should apply to LinuxMCE.  However, this page should be edited to reflect changes to LinuxMCE and remove old references to Pluto.</td></tr> </table>[[Category:GSD]]
 
<table width="100%"> <tr><td bgcolor="#FFCFCF">This page was written by Pluto and imported with their permission when LinuxMCE branched off in February, 2007.  In general any information should apply to LinuxMCE.  However, this page should be edited to reflect changes to LinuxMCE and remove old references to Pluto.</td></tr> </table>[[Category:GSD]]
 
[[Category:Pluto_Devices]]
 
[[Category:Pluto_Devices]]
Generic_Serial_Device (also knowk as '''GSD''') is a Pluto device that allows enduser to do simple programming for RS232, serial USB or network connected devices.
+
Generic_Serial_Device (also knowk as '''GSD''') is a LinuxMCE device that allows end-user to do simple programming for RS232, serial USB or network connected devices.
 
:Note : The information is not quite complete because I'm not the original author.
 
:Note : The information is not quite complete because I'm not the original author.
  
Line 62: Line 62:
 
  end
 
  end
  
There might be more then one class definition in that source. This may happend when child device is itself a GSD device.
+
There might be more then one class definition in that source. This may happen when child device is itself a GSD device.
  
 
=== Command details ===
 
=== Command details ===
When the command from DCE arrives it is routed to specific device and ''cmd_XXX'' is ran. The commands are ran one by one, meaning that you can not run 2 commands simultaneously. This limitation is implied by embedded ruby interpretor which fails if ran from multiple threads.
+
When the command from DCE arrives it is routed to specific device and ''cmd_XXX'' is run. The commands are run one by one, meaning that you cannot run 2 commands simultaneously. This limitation is implied by embedded ruby interpretor which fails if run from multiple threads.
 
DCE commands may have return parameters (well known GetVideoFrame), as ruby can return only 1 parameter the ''@returnParamArray'' was added. You can see that in each method we insert '''@returnParamArray.clear''' then the real code and then '''return @returnParamArray'''. Also some setters are generated (like the one from above) for code readability.
 
DCE commands may have return parameters (well known GetVideoFrame), as ruby can return only 1 parameter the ''@returnParamArray'' was added. You can see that in each method we insert '''@returnParamArray.clear''' then the real code and then '''return @returnParamArray'''. Also some setters are generated (like the one from above) for code readability.
  
Line 77: Line 77:
  
 
== Implementation notes ==
 
== Implementation notes ==
There are several classes that actually needs to be touched. Most of the other classes are just for wrapping DCE objects (like Device, Message, Connection and so on) and exporting them into ruby.
+
There are several classes that actually need to be touched. Most of the other classes are just for wrapping DCE objects (like Device, Message, Connection and so on) and exporting them into ruby.
  
 
Most important classes :
 
Most important classes :
 
* '''RubyDCEDeviceNode''' is actually the Device, responsable with initialisation and partially a message handler.
 
* '''RubyDCEDeviceNode''' is actually the Device, responsable with initialisation and partially a message handler.
 
* '''RubyDCEEmbededClass''' is bridge between DeviceNode and ruby, it has ''CallCmdHandler'' which does the real call to ruby and sends back result.
 
* '''RubyDCEEmbededClass''' is bridge between DeviceNode and ruby, it has ''CallCmdHandler'' which does the real call to ruby and sends back result.
* '''RubyIOManager''' is the one responsable for instantiating RubyDCEDeviceNodes, code retrieval, command handler and serializing ruby calls.
+
* '''RubyIOManager''' is the one responsible for instantiating RubyDCEDeviceNodes, code retrieval, command handler and serializing ruby calls.
 
* '''RubyDCECodeSupplier''' gives you the ruby code
 
* '''RubyDCECodeSupplier''' gives you the ruby code
 
* '''RubyCommandWrapper''' a wrapper for Message
 
* '''RubyCommandWrapper''' a wrapper for Message
Line 88: Line 88:
 
* '''RubySerialIOConnectionWrapper''' helps to wrap Serial and TCP/IP based connection under same interface, see down the hierarchy the connection you need.
 
* '''RubySerialIOConnectionWrapper''' helps to wrap Serial and TCP/IP based connection under same interface, see down the hierarchy the connection you need.
  
There is also some files needed for building a library for ruby with SWIG, let them alone, it should work.
+
There are also some files needed for building a library for ruby with SWIG, let them alone, it should work.
  
The compilation will give you '''Generic_Serial_Device''' and '''Ruby_Generic_Serial_Device.so''', first file is the binary itself, second file is the bridge library and it will be included by ruby (the request for it is in the first line of generated source)
+
The compilation will give you '''Generic_Serial_Device''' and '''Ruby_Generic_Serial_Device.so''', first file is the binary itself, second file is the bridge library and it will be included by ruby (the request for it is in the first line of the generated source)
  
 
== Known issues ==
 
== Known issues ==
  
* Only '''ONE''' command can be executed at a time. This is a limitation of embedded ruby, which should be ran from one thread only.
+
* Only '''ONE''' command can be executed at a time. This is a limitation of embedded ruby, which should be run from one thread only.
* If command was expecting an answer, it will wait about 20 seconds for ruby command to complete, and if command haven't completed yet, it will send empty response.
+
* If command expected an answer, it would wait about 20 seconds for the ruby command to complete, and if the command haven't completed yet, it would send an empty response.
 
* Most of the data are pretty cryptic (like ''device_.devdata_[114]''), you have to look that that number represents (device data id, command id, command parameter id, event id, event parameter id, some other id)
 
* Most of the data are pretty cryptic (like ''device_.devdata_[114]''), you have to look that that number represents (device data id, command id, command parameter id, event id, event parameter id, some other id)
  
Line 154: Line 154:
 
* it has 6 parameters ''cmd_84(data, format, disable_aspect_lock, streamid, width, height)'', parameters ''data'' and ''format'' are also output parameters
 
* it has 6 parameters ''cmd_84(data, format, disable_aspect_lock, streamid, width, height)'', parameters ''data'' and ''format'' are also output parameters
 
* to set output parameters methods like ''data_set'' and ''format_set'' are automatically generated
 
* to set output parameters methods like ''data_set'' and ''format_set'' are automatically generated
* ''device_.devdata_[114]'', ''device_.devdata_[115]'' are username and password to login into camera  
+
* ''device_.devdata_[114]'', ''device_.devdata_[115]'' are the username and password to login into camera  
 
* then it creates HTTP header with basic authorization
 
* then it creates HTTP header with basic authorization
* ''device_.devdata_[2]'' is path to the snapshot (it is configurable because it can change between models)
+
* ''device_.devdata_[2]'' is the path to the snapshot (it is configurable because it can change between models)
 
* it sends the request and waits for the answer
 
* it sends the request and waits for the answer
 
* reads the answer, regexp parse it and gets JPEG content
 
* reads the answer, regexp parse it and gets JPEG content
 
* sets output parameters
 
* sets output parameters
 
* returns @returnParamArray (the return statement is added automatically)
 
* returns @returnParamArray (the return statement is added automatically)

Revision as of 11:23, 15 March 2007

This page was written by Pluto and imported with their permission when LinuxMCE branched off in February, 2007. In general any information should apply to LinuxMCE. However, this page should be edited to reflect changes to LinuxMCE and remove old references to Pluto.

Generic_Serial_Device (also knowk as GSD) is a LinuxMCE device that allows end-user to do simple programming for RS232, serial USB or network connected devices.

Note : The information is not quite complete because I'm not the original author.

Short description

It's a standard DCE device, it only can execute commands and send events (no event handler yet, but Aaron wants it badly).

The main idea is instead of programming a device in C++, compile, link with proper libraries you can edit (in a webpage) few lines of code in ruby which will be executed when device receives that command. It exports several objects into ruby so you'll have access to devicedata, device hierarchy, established connection and so on.

The ruby code for each device template is stored somewhere in InfraredGroup_Command table. To edit the code go to Wizard> Devices > Generic_Serial_Devices on pluto-admin website. You have to add a device from a device template in order to see anything in that page.

Select the device, and click "RubyCodes" button to be able to edit code that will be executed. You should be able to see commands that are specified in device template (like ON/OFF/WhateverElse) and a group of Internal Commands which are used to perform certain actions are to add helper functions.

If the command you are trying to add doesn't show on first page, go to Add/Remove commands and add more commands for your device.

Ruby source code

There are 2 forms of ruby code you can write :

  • standard ruby code (check [1] and [2] for syntax, references and programming guides)
  • short form for conn_.Send
<$"PWON\r"$>
which simply will send the string "PWON\r" to device.

You'll have to quick reload router to get new code. Restarting GSD only will keep old code because the code is supplied by Infrared_Plugin which caches pluto_main and won't update it's cache without full restart of DCERouter. With this code it creates a ruby source which will be interpreted by embedded ruby.

It will create methods called "cmd_CmdIDx" for each command(x) defined in web interface except Private_Method_Listing which will be inserted in the body of the class directly without wrapping.

The source usually looks like (DevID is iPK_Device of specific device, CmdIDx is iPK_Command ) :

require 'Ruby_Generic_Serial_Device'
class Command < Ruby_Generic_Serial_Device::RubyCommandWrapper
end
class Device_DevID < Ruby_Generic_Serial_Device::RubySerialWrapper
def cmd_CmdID1(paramlist)
    @returnParamArray.clear
    ### WEB CODE for CmdID1 GOES HERE ###
    return @returnParamArray
end
def cmd_CmdID2(paramlist)
    @returnParamArray.clear
    ### WEB CODE for CmdID2 GOES HERE ###
    return @returnParamArray
end
...
def cmd_ReceiveCommandForChild(cmd)
    ### Process_Receive Command_For_Child GOES HERE ###
end
...
### Private_Method_Listing GOES HERE ###
...
def initialize()
    super
    @returnParamArray=Array.new
end
### Generated Setters/Getters goes here (may be empty, but usually looks like below) ###
def data_set(value)
    @returnParamArray[19]=value
end

end

There might be more then one class definition in that source. This may happen when child device is itself a GSD device.

Command details

When the command from DCE arrives it is routed to specific device and cmd_XXX is run. The commands are run one by one, meaning that you cannot run 2 commands simultaneously. This limitation is implied by embedded ruby interpretor which fails if run from multiple threads. DCE commands may have return parameters (well known GetVideoFrame), as ruby can return only 1 parameter the @returnParamArray was added. You can see that in each method we insert @returnParamArray.clear then the real code and then return @returnParamArray. Also some setters are generated (like the one from above) for code readability.

Beside usual DCE command you'll see some Internal Commands. Here goes a small list and what they do:

  • Private Method Listing - used to insert all kind of helpers
  • Process IDLE - method executed once in a while (may be used to keep connection alive, scan for new devices on serial bus)
  • Process Incoming Data - called when some data is available
  • Process Receive Command For Child - called if the device has children and those are pretty dummy (like Lights under CM11), so practically the parent will do the job. As a parameter you have cmd which is a wrapper for DCEMessage, so you'll have access to deviceId, senderId, commandID and so on.
  • Process Initialize - called when device is starting (to perform handshake with device or some other initialisation required by protocol)
  • Process Release - to close gracefully connection with device (say "good bye" or something)

Implementation notes

There are several classes that actually need to be touched. Most of the other classes are just for wrapping DCE objects (like Device, Message, Connection and so on) and exporting them into ruby.

Most important classes :

  • RubyDCEDeviceNode is actually the Device, responsable with initialisation and partially a message handler.
  • RubyDCEEmbededClass is bridge between DeviceNode and ruby, it has CallCmdHandler which does the real call to ruby and sends back result.
  • RubyIOManager is the one responsible for instantiating RubyDCEDeviceNodes, code retrieval, command handler and serializing ruby calls.
  • RubyDCECodeSupplier gives you the ruby code
  • RubyCommandWrapper a wrapper for Message
  • GSDMessageProcessing is message translator which will try to run command if it's implemented without translation.
  • RubySerialIOConnectionWrapper helps to wrap Serial and TCP/IP based connection under same interface, see down the hierarchy the connection you need.

There are also some files needed for building a library for ruby with SWIG, let them alone, it should work.

The compilation will give you Generic_Serial_Device and Ruby_Generic_Serial_Device.so, first file is the binary itself, second file is the bridge library and it will be included by ruby (the request for it is in the first line of the generated source)

Known issues

  • Only ONE command can be executed at a time. This is a limitation of embedded ruby, which should be run from one thread only.
  • If command expected an answer, it would wait about 20 seconds for the ruby command to complete, and if the command haven't completed yet, it would send an empty response.
  • Most of the data are pretty cryptic (like device_.devdata_[114]), you have to look that that number represents (device data id, command id, command parameter id, event id, event parameter id, some other id)

Short example

For more details about exported classes and available methods check GSD_Ruby_Interface

Here is a short example from Panasonic IP Camera:

  0:require 'Ruby_Generic_Serial_Device'
  1:class Command < Ruby_Generic_Serial_Device::RubyCommandWrapper
  2:end
  3:class Device_47 < Ruby_Generic_Serial_Device::RubySerialWrapper
  4:#### 84 ####################################################################
  5:def cmd_84(data, format, disable_aspect_lock, streamid, width, height)
  6:@returnParamArray.clear
  7:conn_.Reconnect()
  8:auth_s=device_.devdata_[114]+":"+device_.devdata_[115]
  9:auth_a=Array.new;
 10:auth_s.each{|c| auth_a.push(c)}
 11:
 12:fix_path=device_.devdata_[2];
 13:fix_path='/'+fix_path if(fix_path[0]!='/'[0]);
 14:
 15:s = "GET "+fix_path+" HTTP/1.0\r\n"
 16:s+= "Accept: */*\r\n"
 17:s+= "Authorization: Basic "+auth_a.pack("m").chop+"\r\n"
 18:s+= "\r\n"
 19:
 20:conn_.Send(s)
 21:recv=""
 22:while(true)
 23:  buff=conn_.Recv(16384, 5000)
 24:  if(buff.length() == 0)
 25:     break
 26:   end
 27:   recv = recv +  buff
 28:end
 29:if (recv=~ /^HTTP[^\r\n]+200\sOK.+?\r\n\r\n(.+)$/m)
 30:  data_set($1)
 31:  format_set('jpg')
 32:end
 33:return @returnParamArray
 34:end
...
194:#### START SETTERS ####################################################################
195:def initialize()
196:super
197:@returnParamArray=Array.new
198:end
199:def data_set(value)
200:@returnParamArray[19]=value
201:end
202:def format_set(value)
203:@returnParamArray[20]=value
204:end
205:####  END  SETTERS ####################################################################
206:end

You can see how command 84 (GetVideoFrame) is implemented:

  • it has 6 parameters cmd_84(data, format, disable_aspect_lock, streamid, width, height), parameters data and format are also output parameters
  • to set output parameters methods like data_set and format_set are automatically generated
  • device_.devdata_[114], device_.devdata_[115] are the username and password to login into camera
  • then it creates HTTP header with basic authorization
  • device_.devdata_[2] is the path to the snapshot (it is configurable because it can change between models)
  • it sends the request and waits for the answer
  • reads the answer, regexp parse it and gets JPEG content
  • sets output parameters
  • returns @returnParamArray (the return statement is added automatically)