Status Update:
Well, after much twisting of the brain, we have success.
I have successfully implemented a 'Home Automation Contoller' interface with PLCBUS.
Just what does it do?Well, it's a set of Ruby Objects that 'encapsulate' home automation protocols.
(Actually, it can be used for ANY GSD device requiring bidirectional support)
Why is this good?This is good for many reasons:
- This effectively eliminates the threading issues with Ruby.
- This handles multiple commands simultaneously, and bidirectionally.
(by multiple commands, I mean it implements a command queue for you, so you don't have to.) - When Bidirectional support is needed, these routines automatically handle the 'command=response' queuing.
You determine if a response is accepted for the current command, or if it should be rejected.
If the response is rejected, it is NOT tossed, instead, the routines then take that rejected response and add it as a COMMAND to the queue to be processed later. - These routines have a full error handler, to handle things like Message Timeouts, max. message retransmits, and optionally sends transmission failures to your protocol. You decide what you want to do in that case.
- Tracks the state of devices, and updates them as needed automatically.
This is best explained by example.
Let's say, you send a SETLEVEL command to a device. (say 50%)
Then, you want to turn it off, so you send an OFF command to said device.
The device won't turn off.
Why?
As far as the device is concerned, it IS off. (or it's STATE is OFF/x)
The x being (50%) didn't change the state of the device, therefore, the device assumes it was never turned on!
These routines will automatically track the state of the device. IF a SetLevel command is sent when the device is OFF,
they will automatically send a command (to itself) to set the state to ON before it sends out the SetLevel command.
How do I use these routines?Simple. I'll include a sample class file. Here's What you'll need to do:
- You simply fill in the blanks (protocol info) in the class file.
e.g. when an ON command is received, send THIS out. - You can analyse BOTH the sent command, and the response back to determine if the response is what you were waiting for. If it is, you set the cmdComplete flag to TRUE. If it's not, don't set the flag.
- Certain circumstances will require you to wait for MULTIPLE responses. No Problem. If you determine you need to receive more data, simply set the needMoreInput flag. (Johnny 5 is alive! yeah, I know)
- Again, circumstances may arise when you need to re-send the command..(maybe slightly modified)
and again, you simply set the reSend flag.
How did I do it?With a lot of patience, lots of debug output, and a little magic
Cool! Can I start using it now?no.
Not quite yet.
I still have to nail down some 'usage' details, I know how I want to do it.. but the code (as you can well imagine) is quite complex, and each minor change required MAJOR inspecting, to make sure it doesn't break anything else...
Stay tuned,
Dan.
P.S.
Ok, I'll give you a peek.. Here's some of my comment extracts... keep in mind, they may change...
##############################
#Class RubyCommand
##############################
class RubyCommand
# This class is a container for a protocolObject Instance.
# This class is responsible for the DIRECTION of communication of a specific command.
##############################
#Classe RubyCommands
##############################
#RubyCommands is an ARRAY of RubyCommand
# This class is responsible for keeping the ORDER of commands straight.
# also, for creating the RubyCommand objects.
# TODO: Sometimes, certain commands need to be ignored.
# TODO: Sometimes, certain commands need to INSERT a command to be sent first.
# TODO: Sometimes, certain commands need to APPEND a command to be send right after?
#
#Actions
#we need to define actions for:
# => 0x001 Direction DCE=>GSD/GSD=>DCE
# => 0x002 Command Sent/NotSent
# => 0x004 Response (Received/Rejected)
# => 0x008 Re-send needed(true/false)
# => 0x010 Re-receive needed(true/false)
# => 0x020 IgnoreNextDCE(true/false)
# => 0x040 IgnoreNextGSD(true/false)
# => 0x080 SendThisFirst(true/false)
# => 0x100 SendThisNext(true/false)
#####################
# class ChildDevice #
#####################
# This class holds a child device.
#
# Methods:
# ChildDevice.devdata[] hash of devdata
# ChildDevice.deviceid <int> Device ID
# ChildDevice.template <int> Template ID of Child
# ChildDevice.parent <int> parent ID of Child (doesnt seem to work)
# ChildDevice.state <string> Current state of child
#################
# class Devices #
#################
# This is a HASH collecion of ChildDevice.
#
# Methods:
# Devices.append(device) <ChildDevice> adds a child device to the hash
# Devices[deviceID].<ChildDeviceMethod> this is how you get info.
# eg: d=Devices.new d.append(child102) d[102].state = "ON/100"
#####################
### PLCBUS OBJECT ###
#####################
# This is a generic replaceable object
# This object needs the following methods:
#
# When creating this class, make sure to keep seperate variables
# to hold dce vs gsd. This is so we can compare the variables
# later.
#
# dcein(cmd)
# dcein is a command or event sent from dce
# this method should take the command and prepare
# the output into gsdout(string)
# This command will set the direction to DCEtoGSD
#
# dceout(cmd) cmd is dce Command Object (Command.new)
# This method contains the command to be sent to DCE
#
# gsdin(string)
# gsdin is a command received from GSD
# This method should take the string and prepare
# the output command in dceout
#
# gsdout(string)
# This method contains the output string to be sent to the device
#
# direction(DCEtoGSD|GSDtoDCE)
# This READ ONLY property determines which direction the current command is going
#
# cmdComplete (TRUE|FALSE)
# This is the flag you set when a response for a command is accepted.
# This property allows the user to compare the command vs response.
#
# It is up to the programmer to make sure what needs to be compared, is.
#
# When this is set to true, the command will be removed unless needMoreInput is set.
#
# neetMoreInput (TRUE|FALSE)
# Use this flag to allow multiple response messages to come to this command.
#
# responseNeeded(TRUE|FALSE) this is a read/write property telling the command object
# this command requires a response.
# this is used for bidirectional communication. If this is set, this command will WAIT for a response from the other side.