Results 1 to 6 of 6

Thread: Girder 6 Plugin Writing - 1

  1. #1
    Join Date
    Jan 1998
    Location
    Jupiter, FL
    Posts
    13,553

    Default Girder 6 Plugin Writing - 1

    This is a work in progress. I'd like to document the plugin system and adjust the doc based on your feedback.

    Introduction

    Girder 6 Plugins
    Girder plugins can be written without the use of any compiler by using the two built-in scripting languages. This allows anyone to add their plugins to Girder without any additional investment in development software. The two languages used are Lua for the back-end and Javascript for the front-end.

    Girder Architecture
    Girder is built as a client-server system. That means that the back-end and the front-end are completely separate and communicate via Sockets ( TCP/IP or otherwise ). This split allows a lot of cool things to happen like running Girder on a Raspberry Pi while accessing it from a Windows machine. Also if a front-end crashes the back-end is unaffected. This of course is great for automation. This split however brings some additional work with it for plugin writers as they have to also pass messages back-and-forward. Don't fear however, passing messages is easy and transparent.

    Goal
    The goal of this document is to get started with a simple plugin that exposes the functionality of a PIO-1 or Global Cache like device. Resources. The Girder 6 manual has a lot of detail on the various libraries available. Of special interest will be the transport functionality as a lot of plugins will be using this.
    Last edited by Ron; May 1st, 2015 at 02:44 PM.
    Ron
    No support through PM

  2. #2
    Join Date
    Jan 1998
    Location
    Jupiter, FL
    Posts
    13,553

    Default .plugin files

    .plugin files
    Girder find your plugins by scanning it's Lua subdirectory for files with the extension .plugin. The syntax of this file is a Lua script. Girder expects the following keys in this file.


    • name
    • description
    • backEndComponentManager
    • backEndScriptName
    • frontEndComponentManager
    • frontEndIncludes
    • frontEndScriptName
    • id


    name
    name is a string, which is displayed in the settings dialog

    description
    description is a string that is also displayed in the settings dialog.

    backEndComponentManager
    this is a boolean that indicates if this plugin uses a component manager. Plugins that export devices to the device manager will answer 'true' here otherwise 'false'.

    backEndScriptName
    This is the name of the lua file that holds the definition of the backend part of the plugin. This part is written in Lua. Note that Lua uses . (dots) to separate directories. For example if your plugin lives in the folder Lua/examples/dmPlugin/plugin.lua you'd enter

    Code:
    backEndScriptName = 'examples.dmPlugin.plugin'
    Since Girder runs on platforms with case sensitive filenames please make sure to match your letter case exactly.

    frontEndComponentManager
    This is the companion to backEndComponentManager, typically you'll have a frontEndComponentManager if you have a backEnd one. So answer 'true' here if you do otherwise 'false'

    frontEndIncludes
    This is a list of strings containing the additional javascript files that should be loaded into the script state. Note that here you should use (forward) slashes for the path separators. For example if you have an additional javascript file called Lua/Examples/dmPluign/helpers.js you type

    Code:
    frontEndIncludes = { "Lua/Examples/dmPluign/helpers.js" }
    frontEndScriptName
    This is the file that contains the plugin part of the front-end in javascript. Again here we use slashes to separate folder names.

    id
    The id is a number that Girder uses to track your plugin. You can request a plugin number here. These numbers are free.

    Example

    Here is an example .plugin file which belongs to the examples/dmplugin plugin.
    Code:
    name = "Device Manager Example"
    description = "Device Manager Example driver"
    backEndComponentManager = true
    backEndScriptName = "examples.dmplugin.plugin"
    frontEndComponentManager = true
    frontEndIncludes = { }
    frontEndScriptName = "examples/dmplugin/plugin.js"
    id=104546
    Ron
    No support through PM

  3. #3
    Join Date
    Jan 1998
    Location
    Jupiter, FL
    Posts
    13,553

    Default General Structure of a Back-end plugin

    Plugins have a few functions that need to be implemented for Girder to be able to use them. On a high level that is


    • create/destroy plugin
    • get actions
    • get component manager
    • start / stop


    Let's have a look at a back-end skeleton script. It's doesn't do anything -but- it's a valid plugin.

    Code:
    local base = require("plugin.plugin")
    
    module(...)
    
    base:subclass(_M)
    
    function start ( self )
        base.start(self)
    end
    
    function stop ( self )
        base.stop(self)
    end
    
    function inbound ( self, id, msgId, data )    
    end
    
    function init ( self, plugin )
        base.init(self)
        self.plugin = plugin
        self._actions = {}    
        instance = self
    end
    
    function deinit( self )
        base.deinit(self)
        self._actions = {}
        instance = nil
    end
    The first thing you see is the require('plugin.plugin'). That is the base class of which we inherit. If you are curious go an open that file. It's pretty small. The plugin.plugin class itself is based off 'Class.lua'. The two functions that come from that class are init and deinit. Deinit is called when the plugin is unloaded and init when it is loaded. The first parameter is the self parameter which holds all the member variables for this instance of the object. The init function also gets a parameter called "plugin". This is the encapsulated C++ plugin object of type IGirderDevice.

    start and stop tell the plugin that it's OK to start generating events or to stop generating events. If you have no need for them, you don't even need to implement them as they are in the base class. the actions function returns a array of available actions.
    Ron
    No support through PM

  4. #4
    Join Date
    Jan 1998
    Location
    Jupiter, FL
    Posts
    13,553

    Default Communications

    Communication
    As we have mentioned previously the front-end and the back-end of a plugin could potentially be running on opposite sides of the world. So plugins need a way to communicate between the front-end ( the user facing side ) and the back-end. This is where inbound and self.plugin.sendOutbound come in. Both function has three important parameters, id, msgId and data. The first is the plugin id of the plugin you'd like to send this message to. The second parameter msgId you can freely choose and is for use by your plugin. The third parameter is a string that will hold whatever message data you are sending. We recommend to use JSON messages.

    Code:
    self.plugin.sendOutbound( self.plugin.id, 100, "{ \"hello\": 200; }")
    NOT FINISHED -> SHOULD COME LATER IN EXPLANATION
    Last edited by Ron; May 9th, 2015 at 10:15 AM.
    Ron
    No support through PM

  5. #5
    Join Date
    Jan 1998
    Location
    Jupiter, FL
    Posts
    13,553

    Default Actions

    Actions
    Each plugin can have several actions. These are the actual pieces that do things inside Girder. There are two parts to actions front-end part and the back-end part. Let's look at the backend part first.

    Back-end Action
    The back end action is derived from plugin.action and has the following structure:

    Code:
    local base = require('plugin.action')
    local Promixis = Promixis
    local print = print
    
    module (...)
    
    base:subclass( _M)
    
    subType = 1
    nodeType = Promixis.BaseNode.NodeType.COMMAND_NODE
    
    function triggerCommand ( self, command, event )    
        print("Example Action Trigger")
        print(command.action.sValue1)
        print(command.action.sValue2)
        
    end
    
    function init ( self )
        base.init(self)
    end
    
    function deinit( self )
        base.deinit(self)
    end
    init and deinit are the boiler plate class constructor and destructors. The meat lives inside triggerCommand. Trigger command gets two parameters, command and event. Command is the data that is setup for the action by the front-end action (we'll see that below). The event parameter holds the event that triggered this action. NOTE the event can be nil if the action was triggered by the "Run" menu item inside Girder.

    The command has the following properties
    Name
    Type Description
    actionType number this is equal to the plugin id
    actionSubType number this is the id of this particular action, choose freely
    action Action Object this holds the actual action parameters
    target Target Object this holds the targeting parameters
    state State Object this holds the state object

    The action object is the one that we are mostly interested in. It has the following properties:

    Name
    Type
    Description
    sValue[1,2,3] string sValue1, 2 and 3 parameter use is defined by your plugin
    iValue[1,2,3] string iValue1, 2 and 3 parameter use is defined by your plugin
    bValue[1,2,3] string iValue1, 2 and 3 parameter use is defined by your plugin
    lValue[1,2,3] number this holds the link id in case your action refers to another node on the tree
    fileGUID[1,2,3] string this holds the GUID of the GML tree that the lValue1,2 or 3 refers to.


    The event object holds the following properties:
    Name
    Type Description
    event string This is the event string
    device number This is the event device
    modifier number This holds the key modifier (0,1,2)
    payloads table of strings This holds the payloads that came with this action.

    Name
    Type Description
    actionType number this is equal to the plugin id
    actionSubType number this is the id of this particular action, choose freely
    action Action Object this holds the actual action parameters
    Ron
    No support through PM

  6. #6

    Post

    Hi Ron,

    I hope you'll find time to work on the plugin creation document as I think this is what’s needed for “new” girder users like me self.

    You write “The goal of this document is to get started with a simple plugin that exposes the functionality of a PIO-1 or Global Cache like device.” Will this documentation (when finished) explain all the code need for one or the other, or more in general how they work?

    I find my self scavenging for bits of needed information in the forum and Girder 6 help files (time consuming and hard for a newcomer) maybe the pugin learning could be “fast tracked” by writing one plugin from scratch with all the required steps and accompanying explanations.

    This could/would eliminate the confusion I am experiencing with explanations that are to general and/or from different code examples.

    What I am attempting is to write a plugin for a Sharp Aquos TV that has serial and tcp/ip control capability so this might be a good candidate but a Dennon/Marantz receiver might be an even better one (since more automation geek's own these)!

    Examples of Sharp control codes and replies:

    'MUTE? ' returns '1\r' for mute on and '2\r' for mute off
    'CHUP ' returns 'OK\r' (Channel-up)

    Control codes are always 8 characters (padded with blank spaces when needed).

    I have the TV but not the Marantz receiver. My TV is currently packed up as I am doing a full renovation of my apartment (but the TVs responses are easily emulated).

    Let me know if I can help out in any way!

    Joachim
    Last edited by Yoggi; June 6th, 2015 at 10:00 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •