Author Topic: Starting the Ruby code for PLCBUS  (Read 7766 times)

ddamron

  • Alumni
  • wants to work for LinuxMCE
  • *
  • Posts: 962
    • View Profile
    • My LinuxMCE User Page
Starting the Ruby code for PLCBUS
« on: January 04, 2008, 01:32:15 pm »
I'm starting the framework for the PLCBUS interface.

I'm attempting this one slightly different than the Insteon.. I learned a few gotchas with that code..

This code will be more OOP.

So far, I have a Command class, which will create a command object.  In that object is where I'll store all the specific data related to that command. (eg, which command it is, if it's an event, or command, all the devdata associated with it)

Then, I have a Commands class - a hash of command objects.

Device class will hold all device specific information (child device info, INCLUDING current State {!})

and, you guessed it, a Devices class to hold all the device objects.

That's it so far...
It's 5:30 AM right now here, I'm getting a bit tired.

TTYL

Dan
The only intuitive interface is the nipple.  After that it's all learned.
My other computer is your windows box.
I'm out of my mind.  Back in 5 minutes.
Q:  What's Red and smells like blue paint?

A:  Red Paint.

posterberg

  • Veteran
  • ***
  • Posts: 82
    • View Profile
Re: Starting the Ruby code for PLCBUS
« Reply #1 on: January 04, 2008, 01:37:34 pm »
Super! I really look forward to get my hands on it!

niz23

  • Guru
  • ****
  • Posts: 361
    • View Profile
Re: Starting the Ruby code for PLCBUS
« Reply #2 on: January 04, 2008, 05:31:15 pm »
Great!

Have no plcbus equipment yet.

Still waiting for a pricelist from the chinese manufacturer.
I beleive it will be a lot cheaper to do a bulk order instead of buying from another company in europe since there is no reseller for plcbus stuff in sweden yet.

I also asked if they have solved the local feedback issue when a user change a lightswitch status.
If they modify firmware to do so, it will enable us to get rid of the need to poll switch status.

/niz23

hari

  • Administrator
  • LinuxMCE God
  • *****
  • Posts: 2428
    • View Profile
    • ago control
Re: Starting the Ruby code for PLCBUS
« Reply #3 on: January 04, 2008, 06:25:29 pm »
Great!

Have no plcbus equipment yet.

Still waiting for a pricelist from the chinese manufacturer.
I beleive it will be a lot cheaper to do a bulk order instead of buying from another company in europe since there is no reseller for plcbus stuff in sweden yet.

I also asked if they have solved the local feedback issue when a user change a lightswitch status.
If they modify firmware to do so, it will enable us to get rid of the need to poll switch status.

/niz23
i saw some quote on the homeseer forums: that won't happen soon as there is no easy way to distinguish between one and three phases. Another workaround ist to use scene controllers for every switch, so you get every message as no switches are controlled locally.

best regards,
Hari
rock your home - http://www.agocontrol.com home automation

niz23

  • Guru
  • ****
  • Posts: 361
    • View Profile
Re: Starting the Ruby code for PLCBUS
« Reply #4 on: January 04, 2008, 07:12:20 pm »
hari,

i saw some quote on the homeseer forums: that won't happen soon as there is no easy way to distinguish between one and three phases. Another workaround ist to use scene controllers for every switch, so you get every message as no switches are controlled locally.

best regards,
Hari

I don´t understand. What´s the difference between one or three phases (use of a 3-phase module) What I want is that when I puch the button on a light switch the switch should send status message back to usb<->plcbus controller. I don´t see what that have to do with one ore three phases.

Using a scene controller is another workaround but it introduce the need for a micro module to build in somewhere since the scene controll is unable to direclty control a lod (like a lamp for example).
See http://www.x10-hk.com/store/manual/plcbus/plcbus-4206.pdf

I have to admit that I need to read up some more on plcbus. Maybe the answer to this question is obvious.

/niz23

hari

  • Administrator
  • LinuxMCE God
  • *****
  • Posts: 2428
    • View Profile
    • ago control
Re: Starting the Ruby code for PLCBUS
« Reply #5 on: January 04, 2008, 07:22:06 pm »
I don´t understand. What´s the difference between one or three phases (use of a 3-phase module) What I want is that when I puch the button on a light switch the switch should send status message back to usb<->plcbus controller. I don´t see what that have to do with one ore three phases.
there is a phase coupler for three phase systems. To make it sending out your commands on all phases you set a special bit in the command. You have to do some different ack handling (first ack from the coupler, then ack from the device)
Quote
Using a scene controller is another workaround but it introduce the need for a micro module to build in somewhere since the scene controll is unable to direclty control a lod (like a lamp for example).
See http://www.x10-hk.com/store/manual/plcbus/plcbus-4206.pdf
the micro-module will fit behind the crystal scene controller. There is also a micro module scene controller to fit behind existing switches. The additional price will give you direct status feedback from "local" changes, as the scene controller sends a command to the micro module, and the usb interface sees that command on the bus.
There is also a command to report all switched on devices. That would help polling for local changes with some delay.
Quote
I have to admit that I need to read up some more on plcbus. Maybe the answer to this question is obvious.

/niz23
feel free to ask..

br hari
rock your home - http://www.agocontrol.com home automation

hari

  • Administrator
  • LinuxMCE God
  • *****
  • Posts: 2428
    • View Profile
    • ago control
Re: Starting the Ruby code for PLCBUS
« Reply #6 on: January 04, 2008, 07:56:54 pm »
cool:
Quote
De order die u bij ons heeft geplaats is verzonden op: 4-1-2008 en heeft als verzendcode gekregen...
dan, looks like you get access to the hardware on monday or tuesday ;)

best regards,
Hari
rock your home - http://www.agocontrol.com home automation

ddamron

  • Alumni
  • wants to work for LinuxMCE
  • *
  • Posts: 962
    • View Profile
    • My LinuxMCE User Page
Re: Starting the Ruby code for PLCBUS
« Reply #7 on: January 04, 2008, 08:02:49 pm »
Sweet!
The only intuitive interface is the nipple.  After that it's all learned.
My other computer is your windows box.
I'm out of my mind.  Back in 5 minutes.
Q:  What's Red and smells like blue paint?

A:  Red Paint.

ddamron

  • Alumni
  • wants to work for LinuxMCE
  • *
  • Posts: 962
    • View Profile
    • My LinuxMCE User Page
Re: Starting the Ruby code for PLCBUS
« Reply #8 on: January 06, 2008, 03:37:18 am »
Initial Sample Code for *ANY* HA interface.. (OOP)
The idea here, is to encapsulate everything in objects.
The Command Object will
1.  get all the command info from DCE
2.  figure out what it needs to send out to the GSD
3.  Send the command out,
4.  wait for a result (if required)
5.  Process the result
6.  send commands / events back to DCE
7.  Repeat this procedure IN REVERSE if needed (eg. a command from the HA bus)



It also contains flags so at any time, we can query the status of the command.

The Device Object is all data associated with a particular device, including the current STATE information
(or at least what the code 'thinks' it should be)

The Commands Object is a simple collection of Command Objects

The Devices Object is a simple collection of Device Objects (go figure)

It's still pretty vague, but the structure is starting to show.

Regards,

Dan
Code: [Select]
class Command
 
  def initialize(command)
    #initialize here
    @command = command
    @event = false
    @processing = false
    @cmdsent = false
    @cmdcomplete = false
    @devdata = {}
   
  end
  def cmdSent
    return @cmdsent
  end
  def cmdComplete
    return @cmdcomplete
  end
  def dcecommand=(value)
    @command = value
  end
  def dcecommand
    return @command
  end 
  def processing
    return @processing
  end
  def gsdCommandOut
    return @gsdcommand
  end
  def devdata(pkparam)
    return @devdata[pkparam]
   
  end
  def setdevdata(pkparam,value)
    @devdata[pkparam]= value
  end
  def event
    return @event
  end
  def event=(event, default=false)
    @event = event
  end
end


class Commands
  def initialize
    @commands = []
  end
  def add(command)
    #@cmd = Command.new(command)
    @commands << command
  end
  def remove
    @commands.delete_at(0)
  end
  def execute

   
  end
end


class Device
  def initialize(devid)
    @devdata = {}
    @devid = devid
    @template = 0
    @parent = 0
  end
  def template
    return @template
  end
  def template=(templateid)
      @template = templateid.to_i
  end
  def parent
    return @parentid
  end
  def parent=(parentid)
    @parentid=parentid.to_i
  end
  def setdevdata(pkparam, value)
    @devdata[pkparam] = value.to_s
  end
  def devdata(pkparam)
    return @devdata[pkparam]
  end
  def state
    return @state
  end
  def configuration
    return @configuration
  end
  def configuration=(value)
    @configuration = value
  end
  def devid
    return @devid
  end
end


class Devices
  def initialize
    @devices = {}
    device_.childdevices_.each_key{|c|
      dev = Device.new(c)
     
      dev.devdata[12] =device_.childdevices_[c].devdata_[12].chomp.lstrip.rstrip
      dev.template = device_.childdevices_[c].devtemplid_
      dev.parent = device_.childdevices_[c].parent_
      add(c,dev)
    }
  end
  def add(device)
    @devices[device.devid] = device
  end
  def remove(devid)
    @devices.remove[devid]
  end
end


#Test Code Below:
cmd = Command.new(184)
cmd2 = Command.new(192)
cmd.setdevdata(74, '100')
cmd2.setdevdata(74, '200')
cmd2.event = true
puts cmd.devdata(74)
puts cmd.devdata(100)
cmd.setdevdata(50,'hello')
puts cmd.inspect
puts cmd.dcecommand
puts "Hello"
cmds = Commands.new()
cmds.add(cmd)
puts cmds.inspect
cmds.add(cmd2)
puts cmds.inspect
cmds.remove
puts cmds.inspect
The only intuitive interface is the nipple.  After that it's all learned.
My other computer is your windows box.
I'm out of my mind.  Back in 5 minutes.
Q:  What's Red and smells like blue paint?

A:  Red Paint.

ddamron

  • Alumni
  • wants to work for LinuxMCE
  • *
  • Posts: 962
    • View Profile
    • My LinuxMCE User Page
Re: Starting the Ruby code for PLCBUS
« Reply #9 on: January 06, 2008, 05:46:06 am »
more code
Started implementing the PLCBUS class
with definitions, getters and setters for the packet..

The structure is now starting to become apparent

Let me know what you think.. (I'm still learning Ruby, so if you can optimize it, let me know how!)

Regards,

Dan


class PLCBUS
  attr_reader :HomeCodes, :UnitCodes, :CommandFunctions
  STX=0x02.chr
  ETX=0x03.chr
  HomeCodes = {
    'A'=>0b0000,
    'B'=>0b0001,
    'C'=>0b0010,
    'D'=>0b0011,
    'E'=>0b0100,
    'F'=>0b0101,
    'G'=>0b0110,
    'H'=>0b0111,
    'I'=>0b1000,
    'J'=>0b1001,
    'K'=>0b1010,
    'L'=>0b1011,
    'M'=>0b1100,
    'N'=>0b1101,
    'O'=>0b1110,
    'P'=>0b1111}
  UnitCodes = {
    '1'=>0b0000,
    '2'=>0b0001,
    '3'=>0b0010,
    '4'=>0b0011,
    '5'=>0b0100,
    '6'=>0b0101,
    '7'=>0b0110,
    '8'=>0b0111,
    '9'=>0b1000,
    '10'=>0b1001,
    '11'=>0b1010,
    '12'=>0b1011,
    '13'=>0b1100,
    '14'=>0b1101,
    '15'=>0b1110,
    '16'=>0b1111}
  CommandFunctions = {
    0x00=>'ALL UNIT OFF',
    0x01=>'ALL LTS ON',
    0x02=>'ON #',
    0x03=>'OFF #',
    0x04=>'DIM *#',
    0x05=>'BRIGHT *#',
    0x06=>'ALL LIGHT OFF',
    0x07=>'ALL USER LTS ON',
    0x08=>'ALL USER UNIT OFF',
    0x09=>'ALL USER LIGHT OFF',
    0x0A=>'BLINK *#',
    0x0B=>'FADE STOP #',
    0x0C=>'PRESETDIM *#',
    0x0D=>'STATUS ON *',
    0x0E=>'STATUS OFF',
    0x0F=>'STATUS REQ',
    0x10=>'(R)MASTER ADDRS SETUP *#',
    0x11=>'(T)MASTER ADDRS SETUP *#',
    0x12=>'SCENES ADDRS SETUP *',
    0x13=>'SCENES ADDRS ERASE',
    0x14=>'ALL SCENES ADDRS ERASE *#',
    0x15=>'FOR FUTURE*',
    0x16=>'FOR FUTURE*',
    0x17=>'FOR FUTURE*',
    0x18=>'GET SIGNAL STRENGTH #',
    0x19=>'GET NOISE STRENGTH #',
    0x1A=>'REPORT SIGNAL STRENGTH *',
    0x1B=>'REPORT NOISE STRENGTH *',
    0x1C=>'GET ALL ID PULSE',
    0x1D=>'GET ONLYON ID PULSE',
    0x1E=>'REPORT ALL ID PULSE',
    0x1F=>'REPORT ONLY ON PULSE'}
 
  def initialize()
    @usercode = 0
    @homeunit = 0
    @command = 0
    @data1 = 0
    @data2 = 0
  end
  def command=(value)
    @command = value
  end
  def command
    return HomeCodes[@command]
  end
  def usercode=(value)
    @usercode = value
  end
  def usercode
    return @usercode
  end
  def homeunit=(value)
    @homeunit = HomeCode[value[0].chr]
    @homeunit += UnitCode[value[1].chr]
  end
  def homeunit
    return @homeunit
  end
  def data1=(value)
    @data1 = value
  end
  def data1
    return @data1
  end
  def data2=(value)
    @data2 = value
  end
  def data2
    return @data2
  end
 
  def data
    return (@usercode.chr @homeunit.chr + @command.chr + @data1.chr + @data2.chr)
  end
  def length
    return data.length
   
  end
  def packet
    return STX + length.chr + data + ETX
  end
end





#Command Class 
class RubyCommand
  # Constants:
  CommandType=['Command','Event']
 
  Status=['Initializing', 'Sending Command', 'Waiting for Command ACK', 'Waiting for Response',
    'Receiving Response', 'Processing Response', 'Sending Response', 'Complete', 'Error']
  CommandDirection=['DCEtoGSD', 'GSDtoDCE'] #Direction of Command
 
  DCEtoGSD = 0
  GSDtoDCE = 1
  def initialize(from, to, priority, cmdtype, command)
    #initialize here
    @from = from
    @to = to
    @priority = priority
    @direction = CommandDirection[DCEtoGSD]
    @command = command
    @commandtype = CommandType[0]
    @processing = false
    @cmdsent = false
    @cmdcomplete = false
    @devdata = {}
    @device = Device.new(to)
    @status = Status[0]
  end
  def device=(deviceid)
    @device = Devices(deviceid)
  end
  def cmdSent
    return @cmdsent
  end
  def cmdComplete
    #Boolean: Flag Set when Command has completed.
    return @cmdcomplete
  end
  def dcecommand=(value)
    @command = value
  end
  def dcecommand
    return @command
  end 
  def processing
    return @processing
  end
  def gsdCommandOut
    return @gsdcommand
  end
  def devdata(pkparam)
    return @devdata[pkparam]
   
  end
  def setdevdata(pkparam,value)
    @devdata[pkparam]= value
  end
  def commandtype
    return @commandtype
  end
  def commandtype=(commandtype, default = 'Command')
    if commandtype == 'Command'
      @commandtype = CommandType[0]
    end
    if commandtype == 'Event'
      @commandtype = CommandType[1]
    end
  end
end


class RubyCommands
  def initialize
    @commands = []
  end
  def add(command)
    #@cmd = Command.new(command)
    @commands << command
  end
  def remove
    @commands.delete_at(0)
  end
  def execute

   
  end
  def nitems
    return @commands.nitems
  end
end


class Device
  def initialize(devid)
    @devdata = {}
    @devid = devid
    @template = 0
    @parent = 0
  end
  def template
    return @template
  end
  def template=(templateid)
    @template = templateid.to_i
  end
  def parent
    return @parentid
  end
  def parent=(parentid)
    @parentid=parentid.to_i
  end
  def setdevdata(pkparam, value)
    @devdata[pkparam] = value.to_s
  end
  def devdata(pkparam)
    return @devdata[pkparam]
  end
  def state
    return @state
  end
  def configuration
    return @configuration
  end
  def configuration=(value)
    @configuration = value
  end
  def devid
    return @devid
  end
end


class Devices
  def initialize
    @devices = {}
    device_.childdevices_.each_key{|c|
      dev = Device.new(c)
     
      dev.devdata[12] =device_.childdevices_[c].devdata_[12].chomp.lstrip.rstrip
      dev.template = device_.childdevices_[c].devtemplid_
      dev.parent = device_.childdevices_[c].parent_
      add(c,dev)
    }
  end
  def add(device)
    @devices[device.devid] = device
  end
  def remove(devid)
    @devices.remove[devid]
  end
end


#Test Code Below:
cmd = RubyCommand.new(-1000, 1, 1, 1, 184)
cmd2 = RubyCommand.new(-1000, 1, 1, 1, 192)
cmd.setdevdata(74, '100')
cmd2.setdevdata(74, '200')
cmd2.commandtype = 'Event'
puts cmd.devdata(74)
puts cmd.devdata(100)
cmd.setdevdata(50,'hello')
puts cmd.inspect
puts cmd.dcecommand
puts "Hello"
cmds = RubyCommands.new()
cmds.add(cmd)
puts cmds.inspect
cmds.add(cmd2)
puts cmds.inspect
#Show Queue Length
puts cmds.nitems
#Remove a command from the queue
cmds.remove
puts cmds.inspect
plc = PLCBUS.new
puts plc.HomeCodes.inspect
« Last Edit: January 06, 2008, 06:13:37 am by ddamron »
The only intuitive interface is the nipple.  After that it's all learned.
My other computer is your windows box.
I'm out of my mind.  Back in 5 minutes.
Q:  What's Red and smells like blue paint?

A:  Red Paint.

hari

  • Administrator
  • LinuxMCE God
  • *****
  • Posts: 2428
    • View Profile
    • ago control
Re: Starting the Ruby code for PLCBUS
« Reply #10 on: January 06, 2008, 11:28:57 pm »
the "#" and "*" are not part of the commands.

best regards,
Hari
rock your home - http://www.agocontrol.com home automation

ddamron

  • Alumni
  • wants to work for LinuxMCE
  • *
  • Posts: 962
    • View Profile
    • My LinuxMCE User Page
Re: Starting the Ruby code for PLCBUS
« Reply #11 on: January 06, 2008, 11:59:12 pm »
Heh, haven't got that far yet... but yeah, I know...
just trying to get the basics down..  Still have a few parts of the spec to add..

Do you see what I'm trying to do here?  versus the 'Structured' method of:
1.  receiving a command from DCE
2.  processing the command
3.  sending it out via GSD
4.  waiting for an ACK
5.  waiting for a Response
6.  making sure the response is from the command sent
7.  Processing the Response
8.  sending the appropriate response back to DCE

At any one of those steps, exceptions happen.. and, each command could potentially have a different 'logical flow'

Add to that, (at least with the Insteon) in some circumstances, I needed to receive 2 response messages for a said command.. 1 from the PLM, and 1 from the Device..

If this method works better, I may rewrite the Insteon to use this method.

Regards,

Dan
The only intuitive interface is the nipple.  After that it's all learned.
My other computer is your windows box.
I'm out of my mind.  Back in 5 minutes.
Q:  What's Red and smells like blue paint?

A:  Red Paint.

ddamron

  • Alumni
  • wants to work for LinuxMCE
  • *
  • Posts: 962
    • View Profile
    • My LinuxMCE User Page
Re: Starting the Ruby code for PLCBUS
« Reply #12 on: January 07, 2008, 06:12:32 am »
Status update:

Haven't done much more.. added routines for Process Command for Child and Process Incoming Data..
Both of those routines will directly SET or call methods in a command object in the commands hash.
If the current command[0] (the first command in the hash) has a flag saying it's waiting for a response, AND the direction is proper, then the data is considered a response.  Otherwise, it's considered a command, and a new command object is created.

This procedure works BOTH ways.. the only difference is the DIRECTION flag, which is either DCEtoGSD or GSDtoDCE

Once the command object has been populated with the data (either Command data or response data) it will automatically fire and complete the process, communicating, if needed, to the other side, and setting it's flags as required.

Neat eh?

This allows me to build all the 'Standard logic' into the Command class, while keeping all the 'Custom PLCBUS logic' inside the PLCBUS class. (which, by the way, is also included in the command object)

This technique SHOULD allow me to 'pull' out the PLCBUS class, and add any other HA standard..

As always, I'm open to suggestions...

Regards,

Dan
The only intuitive interface is the nipple.  After that it's all learned.
My other computer is your windows box.
I'm out of my mind.  Back in 5 minutes.
Q:  What's Red and smells like blue paint?

A:  Red Paint.