PLCBUS

From LinuxMCE
Revision as of 01:29, 12 March 2008 by Ddamron (Talk | contribs)

Jump to: navigation, search
Plcbus-logo.jpg

Status

ddamron wrote a GSD for PLCBUS with ruby. Hari will try to get a c++ dce client running.

About

PLCBUS Technology provides you the most perfect Home Automation solutions. Control one lamp minimally to the whole house, all the PLCBUS products range is including Lamp control, Appliance control, Remote telephone control, Computer control and Security System control. Based on the features: “High Reliability”, “Two-Way Communications”, “No rewiring necessary”, etc, PLCBUS Technology will be new Home Automation standard in the following 10-15 years.

Demo video:

Features

  • Reliability
  • 2-way communication
  • Powerline, no rewiring
  • ~200bps
  • 230V/50hz

Interfaces

Command specification

GSD/Ruby Code

RCODE:
   0:require 'Ruby_Generic_Serial_Device'
   1:class Command < Ruby_Generic_Serial_Device::RubyCommandWrapper
   2:end
   3:class Device_110 < Ruby_Generic_Serial_Device::RubySerialIOWrapper
   4:#### 350 ####################################################################
   5:def cmd_350(cmd=nil)
   6:@returnParamArray.clear
   7:### #350
   8:
   9:while(true)
  10:  @buff = conn_.RecvDelimited(0x02.chr,100) #stx
  11:  if(@buff.length() == 0)
  12:    break
  13:  end
  14:  @buff2 = conn_.Recv(1,10) #Length of message
  15:  @buff +=@buff2
  16:  @length = @buff2[0]
  17:  @buff += conn_.Recv(@length + 1, 100)
  18:  debugin(@buff)
  19:  dataIn(@buff)
  20:end
  21:
  22:
  23:# this routine needs to be handled better.
  24:# possibly with regular expressions.
  25:return @returnParamArray
  26:end
  27:#### 351 ####################################################################
  28:def cmd_351(cmd=nil)
  29:@returnParamArray.clear
  30:errorHandler()
  31:return @returnParamArray
  32:end
  33:#### 355 ####################################################################
  34:def cmd_355(cmd=nil)
  35:@returnParamArray.clear
  36:#########################
  37:# Written by Dan Damron #
  38:#########################
  39:
  40:initDevices()
  41:return @returnParamArray
  42:end
  43:#### PRIVATE METHODS ####################################################################
  44:#######################################
  45:# Threaded RubyCommand
  46:# Written by Dan Damron
  47:#######################################
  48:
  49:class Device                                                      #Child Device Holder
  50:  attr_accessor  :devdata, :deviceid, :template, :parent, :onoff, :level
  51:  def initialize()                                               
  52:    @onoff = 'OFF'                                                #State
  53:    @level = 0                                                    #Level
  54:    @devdata = Hash.new                                           #to hold devdata
  55:    @deviceid = @template = @parent = 0
  56:  end
  57:end
  58:
  59:class Devices                                                     #holder for Child Devices
  60:  def initialize
  61:    @devices = Hash.new
  62:  end
  63:  def append(device)                                              #Device Object
  64:    @devices[device.deviceid] = device
  65:  end
  66:  def find(value)                                                 #this method finds the devdata[12] and returns the child ID
  67:    @result = 0
  68:    log('Looking up:' + value.to_s)
  69:    @devices.each_key{|d| 
  70:      if @devices[d].devdata[12] == value
  71:        @result = @devices[d].deviceid 
  72:      end}
  73:    log('In Devices:find() result:' + @result.to_s)
  74:    return @result
  75:  end
  76:  def [](value)                                                   #return the device.
  77:    return @devices[value]
  78:  end
  79:  def log(line)                                                   #logger
  80:    @log = File.open("/var/log/pluto/" + $me.deviceid.to_s + "_Generic_Serial_Device.log", "a")
  81:    @log.putc '('[0]
  82:    @log.putc 'D'[0]
  83:    @log.putc 'E'[0]
  84:    @log.putc 'V'[0]
  85:    @log.putc 'I'[0]
  86:    @log.putc 'C'[0]
  87:    @log.putc 'E'[0]
  88:    @log.putc 'S'[0]
  89:    @log.putc ')'[0]
  90:    @l = line.to_s
  91:    @l.each_byte{|ch|
  92:      @log.putc ch
  93:      if ch == 60
  94:        @log.putc 32
  95:      end
  96:    }
  97:    @log.puts
  98:    @log.close
  99:    @log = nil
 100:  end
 101:end
 102:
 103:
 104:RED = 0x1b.chr + '[31m'
 105:GREEN = 0x1b.chr + '[32m'
 106:YELLOW = 0x1b.chr + '[33m'
 107:BLUE = 0x1b.chr + '[34m'
 108:PURPLE = 0x1b.chr + '[35m'
 109:AQUA = 0x1b.chr + '[36m'
 110:GREY = 0x1b.chr + '[37m'
 111:REDBACK = 0x1b.chr + '[41m'
 112:GREENBACK = 0x1b.chr + '[42m'
 113:YELLOWBACK = 0x1b.chr + '[43m'
 114:BLUEBACK = 0x1b.chr + '[44m'
 115:PURPLEBACK = 0x1b.chr + '[45m'
 116:AQUABACK = 0x1b.chr + '[46m'
 117:GREYBACK = 0x1b.chr + '[47m'
 118:BLACKBACK = 0x1b.chr + '[48m'
 119:DCEtoGSD = 10
 120:GSDtoDCE = 20
 121:NotReady = 1
 122:SendReady = 2
 123:ReceiveReady = 3
 124:Complete = 4
 125:Failed = 5
 126:
 127:
 128:
 129:
 130:##############################
 131:# Methods for $commands[] queue
 132:##############################
 133:def dataIn(value)
 134:  #log('DataIn Entered:')
 135:  $commands.each{|cmd|
 136:    if cmd.state == ReceiveReady                                    #only ReceiveReady cmds.
 137:      if cmd.dataIn(value) == true                                  #this data was accepted if TRUE
 138:        @newcmd = cmd.commandHolder                                 #check for new command
 139:        if @newcmd != nil
 140:          log(YELLOW + 'This command is spawning!!!' + GREY)
 141:          $commands.push @newcmd                                    #if so, push it onto the queue
 142:        end
 143:        if cmd.state == SendReady
 144:          log('DataIn: Ready to Send.')
 145:          dataOutSingleCmd(cmd)
 146:        end
 147:        if cmd.state == Complete
 148:          if cmd.threaded == false
 149:            log(YELLOW + 'Threading RESUMED' + GREY)
 150:            $threadingSuspended = false                                 #allow other commands to now transmit.
 151:          end
 152:          log(PURPLE + 'DataIn:Command Complete!' + GREY)
 153:          $commands.delete(cmd)                                     #remove command if complete
 154:        end
 155:        if cmd.state == Failed
 156:          log(RED + 'DataIn:This Command FAILED:' + GREY)
 157:          log(YELLOW + cmd.inspect + GREY)                          #inspect the failed command
 158:          log('Command Deleted.')
 159:          $commands.delete(cmd)                                     #remove command if failed
 160:        end
 161:        return
 162:      end
 163:    end
 164:  }
 165:  #if we hit here, the data has been rejected by all current commands.
 166:  log('dataIn: New Command:')
 167:  c = RubyCommand.new
 168:  c.dataIn(value) # sets up the command.
 169:  dataOutSingleCmd(c) if c.state == SendReady #check to see if it's ready to send
 170:  $commands.push c.clone                      #push a copy of it onto the queue.
 171:  c = nil
 172:  
 173:  #dataOut()                                                         #safer to call it in turn.
 174:  #log('DataIn Exited:')
 175:end
 176:def dataOut()                                                       #replaces send()
 177:  log('dataOut Entered:')
 178:  #Send out all the commands...
 179:  $commands.each{|cmd|
 180:    dataOutSingleCmd(cmd)
 181:    if cmd.state == Complete
 182:      log(PURPLE + 'DataOut:Command Complete!' + GREY)
 183:      $commands.delete(cmd)
 184:    end
 185:    if cmd.state == Failed
 186:      log(RED + 'DataOut:This Command FAILED:' + GREY)
 187:      log(YELLOW + cmd.inspect + GREY)                          #inspect the failed command
 188:      log('Command Deleted.')
 189:      $commands.delete(cmd)                                     #remove command if failed
 190:    end
 191:
 192:  }
 193:  #log('dataOut Exited:')
 194:end
 195:def dataOutSingleCmd(cmd)
 196:  log('dataOutSingleCmd Entered:')
 197:  if cmd.state == SendReady                                         #send out only if ready.
 198:    @out = cmd.dataOut
 199:    log('got past cmd.dataOut')
 200:    log('@out is type:' + @out.class.to_s)
 201:    case @out.class.to_s
 202:    when 'String'
 203:      log('Threading Suspended = FALSE') if $threadingSuspended == false
 204:      log('Threading Suspended = TRUE') if $threadingSuspended == true
 205:      
 206:      if $threadingSuspended == false
 207:      conn_.Reconnect()
 208:      log('Sending to GSD...')
 209:      debugout(@out)
 210:      conn_.Send(@out)
 211:      debugout(@out)
 212:      conn_.Send(@out)
 213:      sleep $delayBetweenTransmits
 214:      if cmd.threaded == false
 215:        log(YELLOW + 'THREADING SUSPENDED!!!' + GREY)
 216:        $threadingSuspended == true
 217:      end
 218:      else
 219:        #cannot send data out while waiting for a NON threaded response.
 220:      end
 221:    when 'Command'
 222:      log('Sending to DCE...')
 223:      SendCommand(@out)
 224:    when 'NilClass'                                                 #safety first!
 225:      log('This command not ready to send.')
 226:    end
 227:  end
 228:  #log('dataOutSingleCmd Exited:')
 229:end
 230:def log(line)
 231:  $log = File.open("/var/log/pluto/" + device_.devid_.to_s + "_Generic_Serial_Device.log", "a")
 232:  $log.putc '('[0]
 233:  $log.putc '*'[0]
 234:  $log.putc '*'[0]
 235:  $log.putc '*'[0]
 236:  $log.putc ')'[0]
 237:  @l = line.to_s
 238:  @l.each_byte{|ch|
 239:    $log.putc ch
 240:    if ch == 60
 241:      $log.putc 32
 242:    end
 243:  }
 244:  $log.puts
 245:  $log.close
 246:end
 247:def debugout(text)
 248:  work = 'OUT:'
 249:
 250:  for c in 1..text.length
 251:    work += padhex("%X" %text[c-1]) + ' '
 252:  end
 253:  log(GREEN + work + "Length:" + text.length.to_s + GREY)
 254:end
 255:def debug(label, text)
 256:  work = label + ":"
 257:  for c in 1..text.length
 258:    work += padhex("%X" %text[c-1]) + ' '
 259:  end
 260:  log(BLUE + work + "Length:" + text.length.to_s + GREY)
 261:end
 262:def debugin(text)
 263:  work = 'IN:'
 264:
 265:  for c in 1..text.length
 266:    work += padhex("%X" %text[c-1]) + ' '
 267:  end
 268:  log(RED + work + "Length:" + text.length.to_s + GREY)
 269:end
 270:def padhex(hex)
 271:  if hex.length==1
 272:    hex = "0" + hex
 273:  end
 274:  return hex
 275:end
 276:
 277:##############################
 278:#Initialization Routine
 279:##############################
 280:def initDevices()                                                   #Loads Child Devices into Devices
 281:  $threadingSuspended = false
 282:  $timeout = 1                                                  #Global timeout
 283:  $retryAttempts = 10                                           #Global Retry Attempts
 284:  $delayBetweenTransmits = 0.4
 285:  log(YELLOW + 'Loading Child Devices...' + GREY)
 286:  $devices = Devices.new
 287:  device_.childdevices_.keys.each{|c|                           #Populate children
 288:    d = Device.new()                                            #create a new device
 289:    d.deviceid = c                                              
 290:    d.template = device_.childdevices_[c].devtemplid_
 291:    d.parent = device_.childdevices_[c].parent_
 292:    d.devdata = device_.childdevices_[c].devdata_
 293:    $devices.append(d)                                          #add d to $devices
 294:    log('Device:' + d.deviceid.to_s)}
 295:  $me = Device.new()                                            #get MY info
 296:  $me.deviceid = device_.devid_
 297:  $me.parent = device_.parent_
 298:  $me.devdata = []
 299:  $me.devdata = device_.devdata_
 300:  $me.onoff = "ON"
 301:  $me.level = 100
 302:  myusercode = "My Usercode: %X" %$me.devdata[59].to_i          #show in hex form
 303:  log(myusercode)
 304:
 305:  log(YELLOW + 'Done.' + GREY)                                  #devdata[59] = usercode
 306:  log(YELLOW + 'Setting up Command Queue' + GREY)             #dedata[249] =  3phase (boolean)
 307:  $commands = Array.new
 308:  log(YELLOW + 'Done.' + GREY)
 309:  #TODO:  read all the PLC devices to get initial status.
 310:  #TODO:  and report status back to lmce.
 311:end
 312:def errorHandler                                                    # threaded errorhandler
 313:  #Error handling is now handled at the RubyCommand Level.
 314:  if $commands.nitems != 0                                          #log out the commands waiting to be processed, if any.
 315:    @work = YELLOW + 'Command Queue:' + $commands.nitems.to_s
 316:    $commands.each{|cm| @work += ', State:' 
 317:      @work += 'NEW' if cm.state == 1
 318:      @work += 'SendReady' if cm.state == 2
 319:      @work += 'ReceiveReady' if cm.state == 3
 320:      @work += 'Complete' if cm.state == 4
 321:      @work += 'Failed' if cm.state == 5
 322:      @work += GREY + '  '}
 323:    log(@work)
 324:  end
 325:  $commands.each{|cmd|
 326:    case cmd.state                                                  #possible to clear out commands here..
 327:    when Complete
 328:      $commands.delete(cmd)
 329:      #    when SendReady
 330:      #      dataOutSingleCmd(cmd)
 331:    when ReceiveReady
 332:      if cmd.timeout == true                                        #call the timeout and get back error.
 333:        log(RED + 'This Command Timed Out!:' + GREY)
 334:        log(RED + cmd.inspect + GREY)
 335:        $commands.delete(cmd)
 336:      else
 337:        dataOutSingleCmd(cmd)
 338:      end
 339:    end
 340:  }
 341:end
 342:
 343:##############################
 344:#Class RubyCommand
 345:##############################
 346:class RubyCommand
 347:  # This class is a container for a protocolObject Instance.
 348:  # This class is responsible for the DIRECTION of communication of a specific command.
 349:  #
 350:  # Error Handling is also handled in the timeout method.
 351:  #
 352:  # If this command creates a NEW command, this object will allow only ONE creation.
 353:  # This class also controls the state information.
 354:  # Constants:
 355:  DCEtoGSD = 10
 356:  GSDtoDCE = 20
 357:  #Object States:
 358:  NEW = 1
 359:  SendReady = 2
 360:  ReceiveReady = 3
 361:  Complete = 4
 362:  Failed = 5
 363:
 364:
 365:  def initialize
 366:    #log('Initialize Entered:')
 367:    @protocolObject = PLCBUS.new()
 368:    @direction = nil
 369:    @timeoutCounter = 0
 370:    @timeoutRetries = 0
 371:    @state = NEW
 372:    @serialNumber = rand(10000).to_s                #for DCE object
 373:    @newCommandSent = false
 374:    #log('Initialize Exited:')
 375:  end
 376:  def dataIn(cmd)                                                   #Returns TRUE if accepted, FALSE if not.
 377:    #log('dataIn Entered:')
 378:    case @state
 379:    when NEW
 380:      log('dataIn: creating NEW command')
 381:      @savedCommand = cmd
 382:      
 383:      case cmd.class.to_s
 384:      when 'String'                                   # new Command from GSD
 385:        @direction = GSDtoDCE
 386:        @protocolObject.gsdCommandIn(cmd)
 387:        @state = SendReady                          #This command is ready to send.
 388:      when 'Command'                                  # new Command from DCE
 389:        @direction = DCEtoGSD
 390:        @protocolObject.dceCommandIn(cmd)
 391:        @state = SendReady                          #This command is ready to send.
 392:      else
 393:        log('FAILURE IN dataIn - cmd.class not recognized!')
 394:        log('Direction: nil')
 395:        log('class is:' + cmd.class.to_s)
 396:        @state = Failed                             #fail this command. (remove it)
 397:      end
 398:      @result = true                                #returns datain Accepted.
 399:    when ReceiveReady                               #Possible Response
 400:      log('dataIn: Possible Response')
 401:      @result = false
 402:      @class = cmd.class.to_s                       #get this only once.
 403:      case @direction
 404:      when DCEtoGSD
 405:        case @class
 406:        when 'String'                               # from GSD
 407:          #log('dataIn: class is STRING')
 408:          #pass the data to the protocol Object and check for accepted.
 409:          #log('dataIn: executing gsdResponseIn')
 410:          if @protocolObject.gsdResponseIn(cmd) == true
 411:            log('response Accepted.')
 412:            @result = true
 413:            if @protocolObject.responseNeeded == true
 414:              log('Command not yet complete.')
 415:              @state = ReceiveReady                   # ProtocolObject needs another response.
 416:            else
 417:              log('Command Complete.')
 418:              @state = Complete                       # ProtocolObject is complete.
 419:            end
 420:
 421:          else
 422:            log('response Rejected!')
 423:          end
 424:        when 'Command'                                # from DCE
 425:          #log('dataIn: class is COMMAND')
 426:          @result = false                           # cmd is NOT for me.
 427:        else
 428:          log('FAILURE IN dataIn - cmd.class not recognised!')
 429:          log('Direction: DCEtoGSD')
 430:          log('class is:' + @class.to_s)
 431:
 432:          @result = false
 433:        end
 434:
 435:      when GSDtoDCE
 436:        case @class
 437:        when 'String'                                 # from GSD
 438:          @result = false                           # not for me.
 439:        when 'Command'                                # POSSIBLE when sending commands to self.
 440:          if cmd.params_[999] == @serialNumber
 441:            @result = true                          #I sent this command!
 442:            log('Caught Command Sent to Self!')
 443:          end
 444:        else
 445:          log('FAILURE IN dataIn - cmd.class not recognised!')
 446:          log('Direction: GSDtoDCE')
 447:          log('class is:' + @class.to_s)
 448:
 449:        end
 450:      end
 451:      #log('dataIn: returning TRUE (exited)') if @result == true
 452:      #log('dataIn: returning FALSE (exited)') if @result == false
 453:      return @result
 454:    end
 455:  end
 456:  def dataOut
 457:    log('dataOut Entered:')
 458:    if @state == SendReady
 459:      case @direction
 460:      when DCEtoGSD                                   #Send GSD String
 461:        @work = @protocolObject.gsdout
 462:        log('dataOut:  Setting @state to Receive Ready')
 463:        @state = ReceiveReady
 464:      when GSDtoDCE
 465:        @work = @protocolObject.dceout
 466:        if @work.class.to_s == 'Command'               #check for valid Object (returns nil if not ready)
 467:          if @work.devidto_ == @work.devidfrom_       #check for command to self
 468:            log('Detected attempting to send to self')
 469:            @work.devdata_[999] = @serialNumber       #this command is to self.  Serialize it.
 470:          end
 471:        end
 472:        log('dataOut:  Setting @state to Complete')
 473:        @state = Complete                             #DCE does not send responses. (yet)
 474:      else
 475:        log('Attempted DataOut with uninitialized Object')
 476:        @state = Failed
 477:        @work = nil
 478:      end
 479:      log('dataOut: Returning @work')
 480:      return @work
 481:    else
 482:      log('This command is not ready to send.')
 483:    end
 484:    log('DataOut Exited:')
 485:  end
 486:  def timeout                                                       #called ONLY when @state == ReceiveReady
 487:    @result = false                                 #returns TRUE if command times out.
 488:    @timeoutCounter +=1                             #ticker += 1
 489:    if @timeoutCounter == $timeout                  #global variable defined in init.
 490:      @timeoutCounter = 0                           #reset ticker
 491:      @timeoutRetries += 1                          #increment Retries..
 492:      if @timeoutRetries == $retryAttempts          #global variable defined in init.
 493:        @state = Failed
 494:        @result = true
 495:      else
 496:        log('Attempt:' + (@timeoutRetries + 1).to_s)
 497:        if @state == ReceiveReady
 498:          @state = SendReady
 499:        end
 500:      end
 501:      
 502:    end
 503:    log('Tick...')
 504:    return @result
 505:  end
 506:  def state
 507:    return @state
 508:  end
 509:  def direction()
 510:    return @direction
 511:  end
 512:  def responseNeeded()
 513:    return @protocolObject.responseNeeded
 514:  end
 515:  def commandHolder()                                               #This is a holder of self.  Used when inserting another command.
 516:    #allow only ONE command to be returned.
 517:    @holder = @protocolObject.commandHolder
 518:    if @holder != nil
 519:      if @newCommandSent == false
 520:        #we can send the command here.
 521:        @newCommandSent = true  
 522:        return @holder
 523:      end
 524:    end
 525:    return nil
 526:  end
 527:  def threaded
 528:    return @protocolObject.threaded
 529:  end
 530:  #  def packCommand(value)  # this is used by protocolObject to pack a command.
 531:  #    if value.class.to_s == 'Command'
 532:  #      #packing a DCE Object..
 533:  #      self.dcein(value)
 534:  #      #override the direction...
 535:  #      @direction = GSDtoDCE
 536:  #      #flag the command ready to be sent.
 537:  #      @sendReady = true
 538:  #    end
 539:  #    if value.class.to_s == 'String'
 540:  #      #packing a GSD object
 541:  #      self.gsdin(value)
 542:  #      #override the direction...
 543:  #      @direction = DCEtoGSD
 544:  #      #flag the command ready to be sent.
 545:  #      @sendReady = true
 546:  #    end
 547:  #  end
 548:  #  def hasNewCommand()
 549:  #    return @hasNewCommand
 550:  #  end
 551:  def log(line)                                                     #support method.
 552:    @log = File.open("/var/log/pluto/" + $me.deviceid.to_s + "_Generic_Serial_Device.log", "a")
 553:    @log.putc '('[0]
 554:    @log.putc 'C'[0]
 555:    @log.putc 'o'[0]
 556:    @log.putc 'm'[0]
 557:    @log.putc 'm'[0]
 558:    @log.putc 'a'[0]
 559:    @log.putc 'n'[0]
 560:    @log.putc 'd'[0]
 561:
 562:    @log.putc ')'[0]
 563:    @l = line.to_s
 564:    @l.each_byte{|ch|
 565:      @log.putc ch
 566:      if ch == 60
 567:        @log.putc 32
 568:      end
 569:    }
 570:    @log.puts
 571:    @log.close
 572:    @log = nil
 573:  end
 574:end
 575:
 576:
 577:
 578:#####################
 579:### PLCBUS OBJECT ### 
 580:#####################
 581:# This is a generic replaceable object
 582:# This object needs the following methods:
 583:# 
 584:# Initialize() 
 585:# 
 586:#   What it does:
 587:#     any Object initialization you may need.
 588:#     
 589:#   When is this called:
 590:#     Upon Object creation.
 591:#     
 592:#   What you will get:
 593:#     nothing.
 594:#     
 595:#   What you will return:
 596:#     nothing.
 597:#     
 598:# 
 599:# dceCommandIn(cmd)
 600:#   
 601:#   What is does:
 602:#     sends you a command object to allow you to create gsdOut().
 603:#     
 604:#   What you will get:
 605:#     cmd (Command Object)
 606:#   
 607:#   What you need to return:
 608:#     nothing (ignored)
 609:#     
 610:#   When you will get this:
 611:#     executed when a new DCE command is sent to you.
 612:# 
 613:#     
 614:# dceResponseIn(cmd)
 615:#   
 616:#   What it does:
 617:#     Sends you a DCE object to compare with the command.
 618:#     
 619:#   What you will get:
 620:#     cmd (Command Object)
 621:#   
 622:#   What you need to return:
 623:#     TRUE if you accept this response.
 624:#     FALSE if you do NOT accept this response.
 625:#     
 626:#   When you will get this:
 627:#     executed when a new DCE response is ready.
 628:# 
 629:# 
 630:# gsdCommandIn(value)
 631:#   
 632:#   What it does:
 633:#     sends you a String to allow you to create a dceOut().
 634:#     
 635:#   What you will get:
 636:#     value (String Object)
 637:#   
 638:#   What you need to return:
 639:#     nothing (ignored)
 640:#     
 641:#   When you will get this:
 642:#     executed when a new GSD command is sent to you.
 643:# 
 644:#     
 645:# gsdResponseIn(value)
 646:# 
 647:#   What it does:
 648:#     Sends you a GSD Response to compare with the command.  
 649:#   What you will get:
 650:#     cmd (Command Object)
 651:#   
 652:#   What you need to return:
 653:#     TRUE if you accept this response.
 654:#     FALSE if you do NOT accept this response.
 655:#     
 656:#   When you will get this:
 657:#     executed when a new GSD response is ready.
 658:#
 659:# dceout() 
 660:#   
 661:#   What it does:
 662:#     Creates a Command Object to send to DCE.
 663:# 
 664:#   What you will get:
 665:#     nothing.
 666:#     
 667:#   What you need to return:
 668:#     a Command Object representing the previous gsdCommandIn.
 669:# 
 670:#   When is this called:
 671:#     After a gsdCommandIn has been sent.
 672:# gsdout()
 673:# 
 674:#   What it does:
 675:#     creates a String Object to send to GSD.
 676:#     
 677:#   What you will get:
 678:#     nothing.
 679:#     
 680:#   What you will return:
 681:#     a String Object representing the previous dceCommandIn.
 682:#
 683:#   When is this called:
 684:#     After a dceCommandIn has been sent.
 685:# 
 686:# 
 687:# Properties:
 688:# 
 689:# @responseAccepted
 690:#   
 691:#   What it does:
 692:#     signals that the current response is accepted as valid.
 693:#   
 694:#   When you should set this:
 695:# 
 696:# @responseNeeded
 697:# 
 698:#   What it does:
 699:#     signals when you need a response.        
 700:#   
 701:# @commandHolder
 702:#
 703:#   What it does:
 704:#     If you need to create a new command, this is the holder for it.
 705:#
 706:
 707:class PLCBUS
 708:  attr_reader :responseAccepted, :responseNeeded, :commandHolder, :threaded
 709:  #Conatants:
 710:  STX=0x02.chr
 711:  ETX=0x03.chr
 712:  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}
 713:  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}
 714:  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',
 715:    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 ONLY ON ID PULSE',0x1E=>'REPORT ALL ID PULSE',0x1F=>'REPORT ONLY ON PULSE'}
 716:
 717:  
 718:  def initialize()
 719:    # declare gsd variables
 720:    @home = 0 #these two varianles determine the TO or FROM address
 721:    @unit = 0 #these two variables determine the TO or FROM address
 722:    @command = 0
 723:    @usercode = $me.devdata[59].to_i
 724:    #@cmdReprq = $me.devdata[260]      #3Phase on Dan's system
 725:    @cmdReprq = $me.devdata[249]      #3Phase on Hari's system
 726:    @data1 = 0
 727:    @data2 = 0
 728:    @cmdAckPulse = false
 729:    @cmdLink = false
 730:    #declare DCE variables
 731:    @dceto = 0
 732:    @dcefrom = 0
 733:    @dcepriority = 0
 734:    @dcecmdtype = 0
 735:    @dcecmdid = 0
 736:    @dceparams = {}
 737:    @responseNeeded = false
 738:    @responseAccepted = false
 739:    @gsdparams = {}
 740:    #@gsdparams[120] = '1' # hack to force command is useless...
 741:    @commandHolder = nil
 742:    #log('Leaving Initialize')
 743:    @threaded = true
 744:  end
 745:  def dceCommandIn(cmd)
 746:    log('dceCommandIn Entered:')
 747:    @dcehomeunit = $devices[cmd.devidto_].devdata[12][0..1]
 748:    @dcehome = HomeCodes[$devices[cmd.devidto_].devdata[12][0].chr].to_s
 749:    @dceunit = UnitCodes[$devices[cmd.devidto_].devdata[12][1].chr].to_s
 750:    @dcefrom = cmd.devidfrom_
 751:    @dceto = cmd.devidto_
 752:    @dcepriority = cmd.priority_
 753:    @dcecmdtype = cmd.type_
 754:    @dcecmdid = cmd.id_
 755:    @dceparams = cmd.params_
 756:
 757:  end
 758:  def dceResponseIn(cmd)                                        # Returns TRUE if response is accepted.
 759:    log('dceResponseIn Entered:')
 760:    log(cmd.inspect)
 761:    return true                           #ignored in PLCBUS
 762:  end
 763:  def gsdCommandIn(value)
 764:    log('gsdCommandIn Entered:')
 765:    debug('Value:', value)
 766:    @gsdusercode = value[2]
 767:    @gsdhomeunit = ("%X" %value[3])
 768:    @gsdhome = @gsdhomeunit[0].chr
 769:    @gsdunit = @gsdhomeunit[1].chr
 770:    @gsdcommand = value[4]
 771:    @gsdcmdAckPulse = true if (value[4] & 0x20) == 0x20
 772:    @gsdcmdReprq = true if (value[4] & 0x40) == 0x40
 773:    @gsdcmdLink = true if (value[4] & 0x80) == 0x80
 774:    @gsddata1 = value[5]
 775:    @gsddata2 = value[6]
 776:    if value[1] == 6
 777:      rxtxSwitchRegister(value[7])   #set switches if they exist
 778:    end
 779:  end
 780:  def gsdResponseIn(value)                                      #returns TRUE if response is accepted.
 781:    #no need to store response.. just compare.
 782:    #negative logic here
 783:    @result = true
 784:    #log('gsdResponseIn Entered:')
 785:    #debug('Value:', value)
 786:    if @usercode == value[2]
 787:      #log('Usercode Check!')
 788:    else
 789:      log('Usercode compare failed')
 790:      @result = false
 791:    end
 792:    if dcehomeunit[0] == value[3]
 793:      #log('Home and Unit CHECK!')
 794:    else
 795:      log('Home and Unit compare failed')
 796:      @result = false
 797:    end
 798:    if dcecommand(@dcecmdid)[0] == value[4]
 799:      #log('PLCBus Command CHECK!')
 800:    else
 801:      log('PLCBus Command compare failed')
 802:      @result = false
 803:    end
 804:    if value[1] == 6
 805:      #log('Setting rxtxRegister')
 806:      rxtxSwitchRegister(value[7])
 807:    else
 808:      log('no rxtxRegister')
 809:      @result = false
 810:    end
 811:    #log('gsdResponseIn returning TRUE') if @result == true
 812:    #log('gsdResponseIn returning FALSE') if @result == false
 813:    return @result
 814:  end
 815:  def dceout                                                    # Returns the GSD values as a DCE command.
 816:    @myfrom = gsdhomeunit
 817:    if @myfrom == 0
 818:      log('I DONT KNOW THIS DEVICE ID')
 819:    else
 820:      @mycommandout = gsdcommand
 821:      log('IN PLCBUS:DCEOUT')
 822:      log('         From: ' + @myfrom.to_s)
 823:      log('           To: -1001')
 824:      log('     Priority: 1')
 825:      log('      CmdType: 2')
 826:      log('        CmdId: ' + @mycommandout.to_s)
 827:      log('Devdata Below:')
 828:      @gsdparams.each_pair{|k,v|
 829:        log('    devdata_[' + k.to_s + '] = ' + v)
 830:      }
 831:      @cmdout = Command.new(@myfrom, -1001, 1, 2, @mycommandout)
 832:      @gsdparams.each_pair{|k,v| @cmdout.params_[k]=v}
 833:      #      @responseNeeded = false
 834:      #      @responseAccepted = true
 835:      return @cmdout
 836:    end
 837:  end
 838:  def gsdout                                                    # Returns the DCE values as a gsd string
 839:    #log('IN PLCBUS:GSDOUT')
 840:    log('SETTING THREADING to FALSE in PLCBUS:GSDOUT:')
 841:    @threaded = false
 842:    @gsdout = @usercode.chr
 843:    @gsdout += dcehomeunit + dcecommand(@dcecmdid) + @data1.chr + @data2.chr
 844:    @gsdout = @gsdout.length.chr + @gsdout
 845:    @gsdout = STX + @gsdout + ETX
 846:    #debug('Data Out', @gsdout)
 847:    return @gsdout
 848:  end
 849:  def checkResponse(cmd)   #old?? fired when a response was accepted.
 850:    log('WARNING: CheckResponse was CALLED')
 851:    #    #this routine needs to set ONE flag:
 852:    #    #responseNeeded (true/false) if we need more data
 853:    #    if cmd.class.to_s == 'String'
 854:    #      #we are checking a GSD command.
 855:    #
 856:    #    end
 857:    #    if cmd.class == Command
 858:    #      #we are checking a DCE command.
 859:    #      if @r_ack_sw == true
 860:    #        #here is a good place to check the data1 and data2 values
 861:    #        #to make sure the proper device responded.
 862:    #        #
 863:    #        #PLCBUS command complete
 864:    #        log('Received Ack from PLCBUS device.')
 865:    #        @responseNeeded = false
 866:    #        # attempt to set device info here.
 867:    #        case @dcecmdid
 868:    #        when 192 # ON
 869:    #          $devices[@dceto].onoff = "ON"
 870:    #          if $devices[@dceto].level != 100
 871:    #            $devices[@dceto].level = 100
 872:    #            log('HAVE TO SEND A SETLEVEL=100')
 873:    #          end
 874:    #        when 193 # OFF
 875:    #          $devices[@dceto].onoff = "OFF"
 876:    #          if $devices[@dceto].level != 0
 877:    #            $devices[@dceto].level = 0
 878:    #            log('HAVE TO SEND A SETLEVEL = 0')
 879:    #            log('**************************')
 880:    #            log('**************************')
 881:    #            log('**************************')
 882:    #            log('**************************')
 883:    #            @commandHolder = RubyCommand.new()
 884:    #            #pack a new DCE command with the Event..
 885:    #            @mycmd = Command.new(@dceto, @dceto, 1, 1, 184)
 886:    #            @mycmd.params_[76] = '0'
 887:    #            @commandHolder.packCommand(@mycmd)
 888:    #
 889:    #          end
 890:    #        when 184 # SetLevel
 891:    #          if @dceparams[76].to_i == 0
 892:    #            #dimming to OFF
 893:    #            if $devices[@dceto].onoff == 'ON'
 894:    #              log('HAVE TO SEND AN OFF to DCE')
 895:    #              log('**************************')
 896:    #              log('**************************')
 897:    #              log('**************************')
 898:    #              log('**************************')
 899:    #              @commandHolder = RubyCommand.new()
 900:    #              #pack a new DCE command with the Event..
 901:    #              @mycmd = Command.new(@dceto, -1001, 1, 2, 40)
 902:    #              @mycmd.params_[10] = '0'
 903:    #              @commandHolder.packCommand(@mycmd)
 904:    #              #
 905:    #              
 906:    #              #@commandHolder = Command.new(@dcefrom, -1001, 1, 2, 40)
 907:    #              #@commandHolder.params_[10] = '0'
 908:    #              $devices[@dceto].onoff = 'OFF'
 909:    #              
 910:    #            end
 911:    #              
 912:    #          else
 913:    #            #dimming to ON
 914:    #            if $devices[@dceto].onoff == 'OFF'
 915:    #              log('HAVE TO SEND AN ON to DCE')
 916:    #              log('**************************')
 917:    #              log('**************************')
 918:    #              log('**************************')
 919:    #              log('**************************')
 920:    #              @commandHolder = RubyCommand.new()
 921:    #              @mycmd = Command.new(@dceto, -1001, 1, 2, 40)
 922:    #              @mycmd.params_[10] = '1'
 923:    #              @commandHolder.packCommand(@mycmd)
 924:    #              #@commandHolder = Command.new(@dcefrom, -1001, 1, 2, 40)
 925:    #              #@commandHolder.params_[10] = '1'
 926:    #
 927:    #              $devices[@dceto].onoff = 'ON'
 928:    #            end
 929:    #          end
 930:    #          $devices[@dceto].level = @dceparams[76].to_i
 931:    #        end
 932:    #        log('Device:' + @dceto.to_s)
 933:    #        log('on/off:' + $devices[@dceto].onoff)
 934:    #        log(' level:' + $devices[@dceto].level.to_s)
 935:    #      end
 936:    #      if @r_itself == true
 937:    #        #heard myself send the response.
 938:    #        log('I heard myself send the command properly.')
 939:    #        @responseNeeded = true
 940:    #      end
 941:    #
 942:    #    end
 943:    @responseAccepted = true
 944:  end
 945:  #Support Routines for PLCBUX
 946:  def dcecommand(value) #DCE value is DCE Command.
 947:    #user code is in devdata[59] Configuration
 948:    #devdata[250] 3phase (boolean)
 949:    @work = 0
 950:    #log('DCE Command is' + value.to_s)
 951:    case value
 952:    when 192 #ON
 953:
 954:      @cmdLink = false
 955:      @cmdReprq = false
 956:      @cmdAckPulse = true
 957:      @data1 = 0 #Fade Rate
 958:      @work = 0x80 if @cmdling == true
 959:      @work += 0x40 if @cmdReprq == true
 960:      @work += 0x20 if @cmdAckPulse == true
 961:      @command =@work + CommandFunctions.index("ON")
 962:    when 193 #OFF
 963:      @cmdLink = false
 964:      @cmdReprq = false
 965:      @cmdAckPulse = true
 966:      @data1 = 0 #Fade Rate
 967:      @work = 0x80 if @cmdling == true
 968:      @work += 0x40 if @cmdReprq == true
 969:      @work += 0x20 if @cmdAckPulse == true
 970:      @command =@work + CommandFunctions.index("OFF")
 971:    when 184 # SETLEVEL
 972:      @cmdLink = false
 973:      @cmdReprq = false
 974:      @cmdAckPulse = true
 975:      @data1 = @dceparams[76].to_i #dim level
 976:      @data2 = 2 #Fade Rate
 977:      @work = 0x80 if @cmdling == true
 978:      @work += 0x40 if @cmdReprq == true
 979:      @work += 0x20 if @cmdAckPulse == true
 980:      @data2 = 2#Fade Rate
 981:      @command =@work + CommandFunctions.index("PRESETDIM")
 982:    end
 983:    #how convert it into a integer - currently it a HEX string.
 984:    #log('PLC Command is:' + @command.to_s + 'DECIMAL')
 985:    return @command.chr
 986:  end
 987:  def gsdcommand #returns DCE EVENT!! and compiles params
 988:
 989:    #log('IN DCECommand')
 990:    
 991:    case CommandFunctions[@gsdcommand]
 992:    when 'ALL UNIT OFF'
 993:      @gsdparams[10] = '0'
 994:      return 48
 995:    when 'ALL LTS ON'
 996:      @gsdparams[10] = '1'
 997:      return 48
 998:    when 'ON'
 999:      @gsdparams[10] = '1'
1000:      return 48
1001:    when 'OFF'
1002:      @gsdparams[10] = '0'
1003:      return 48
1004:    when 'DIM'
1005:      @gsdparams[10] = @gsddata1 # set Brightness
1006:      return 48
1007:
1008:    when 'BRIGHT'
1009:      @gsdparams[10] = @gsddata1 # set Brightness
1010:      return 48
1011:
1012:    when 'ALL LIGHT OFF'
1013:      @gsdparams[10] = '0'
1014:      return 48
1015:    when 'ALL USER LTS ON'
1016:      @gsdparams[10] = '1'
1017:      return 48
1018:    when 'ALL USER UNIT OFF'
1019:      @gsdparams[10] = '0'
1020:      return 48
1021:    when 'ALL USER LIGHT OFF'
1022:      @gsdparams[10] = '0'
1023:      return 48
1024:    when 'BLINK'
1025:    when 'FADE STOP'
1026:    when 'PRESETDIM'
1027:      @gsdparams[10] = @gsddata1
1028:      return 48
1029:    when 'STATUS ON'
1030:    when 'STATUS OFF'
1031:    when 'STATUS REQ'
1032:    when '(R)MASTER ADDRS SETUP'
1033:    when '(T)MASTER ADDRS SETUP'
1034:    when 'SCENES ADDRS SETUP'
1035:    when 'SCENES ADDRS ERASE'
1036:    when 'ALL SCENES ADDRS ERASE'
1037:    when 'FOR FUTURE'
1038:    when 'GET SIGNAL STRENGTH'
1039:    when 'GET NOISE STRENGTH'
1040:    when 'REPORT SIGNAL STRENGTH'
1041:    when 'REPORT NOISE STRENGTH'
1042:    when 'GET ALL ID PULSE'
1043:    when 'GET ONLY ON ID PULSE'
1044:    when 'REPORT ALL ID PULSE'
1045:    when 'REPORT ONLY ON PULSE'
1046:    else
1047:      log('UNKNOWN COMMAND RESPONSE in PLCBUS OBJECT')
1048:      return 0
1049:    end
1050:
1051:  end
1052:  def dcehomeunit() # returns GSD homeunit byte from DCE homeunit
1053:    @work = @dcehome + @dceunit
1054:    return (@work.hex.chr)
1055:  end
1056:  def gsdhomeunit() #returns DCE Child ID from gsd homeunit
1057:    #@gsdhomeunit holds the data
1058:    @work = HomeCodes.index(@gsdhomeunit[0].chr.to_i)
1059:    @work += UnitCodes.index(@gsdhomeunit[1].chr.to_i)
1060:    return $devices.find(@work)
1061:  end
1062:  def rxtxSwitchRegister(value)
1063:    #log('setting RX_TX_SWITCH')
1064:    if (value & 0x40) == 0x40
1065:      #log('r_id_sw is set') 
1066:      @r_id_sw = true
1067:    else
1068:      #log('r_id_sw is reset') 
1069:      @r_id_sw = false
1070:    end
1071:    if (value & 0x20) == 0x20
1072:      #log('r_ack_sw is set') 
1073:      @r_ack_sw = true 
1074:      @responseNeeded = false
1075:    else
1076:      #log('ResponseNeeded set to TRUE')
1077:      @responseNeeded = true
1078:      #log('r_ack_sw is reset') 
1079:      @r_ack_sw = false
1080:    end
1081:    if (value & 0x10) == 0x10
1082:      #log('r_itself is set') 
1083:      @r_itself = true 
1084:      log('I heard myself Transmit this command on the PLCBUS.')
1085:    else
1086:      #log('r_itself is reset') 
1087:      @r_itself = false 
1088:      log('This Command is transmitted from an OUTSIDE SOURCE!')
1089:    end
1090:    if (value & 0x8) == 0x8
1091:      #log('r_risc is set') 
1092:      @r_risc = true 
1093:    else
1094:      #log('r_risc is reset') 
1095:      @r_risc = false
1096:    end
1097:    if (value & 0x4) == 0x4
1098:      #log('r_sw is set') 
1099:      @r_sw = true 
1100:    else
1101:      #log('r_sw is reset') 
1102:      @r_sw = false
1103:    end
1104:    
1105:  end
1106:  #Generic Support Routines
1107:  def log(line)
1108:    @log = File.open("/var/log/pluto/" + $me.deviceid.to_s + "_Generic_Serial_Device.log", "a")
1109:    @log.putc '('[0]
1110:    @log.putc 'P'[0]
1111:    @log.putc 'L'[0]
1112:    @log.putc 'C'[0]
1113:    @log.putc ')'[0]
1114:    @l = line.to_s
1115:    @l.each_byte{|ch|
1116:      @log.putc ch
1117:      if ch == 60
1118:        @log.putc 32
1119:      end
1120:    }
1121:    @log.puts
1122:    @log.close
1123:    @log = nil
1124:  end
1125:  def debug(label, text)
1126:    work = label + ":"
1127:    for c in 1..text.length
1128:      work += padhex("%X" %text[c-1]) + ' '
1129:    end
1130:    log(BLUE + work + "Length:" + text.length.to_s + GREY)
1131:  end
1132:  def padhex(hex)
1133:    if hex.length==1
1134:      hex = "0" + hex
1135:    end
1136:    return hex
1137:  end
1138:end
1139:def cmd_ReceiveCommandForChild(cmd)
1140:### #484 Process Receive Command for Child
1141:log('DCE Command Received')
1142:
1143:dataIn(cmd)
1144:end
1145:#### 760 ####################################################################
1146:def cmd_760(id, pk_command, parameters, cmd=nil)
1147:@returnParamArray.clear
1148:log('DCE Send Command to Child 760 Received')
1149:
1150:dcein760(cmd)
1151:return @returnParamArray
1152:end
1153:#### 776 ####################################################################
1154:def cmd_776(arguments, cmd=nil)
1155:@returnParamArray.clear
1156:#Simulate a gsdin command...
1157:#[31mIN:02 06 22 30 02 64 00 0C 03 Length:9
1158:@simulation = 0x02.chr + 0x06.chr + 0x22.chr + 0x30.chr + 0x02.chr
1159:@simulation += 0x64.chr + 0x00.chr + 0x0C.chr + 0x03.chr
1160:log(AQUA + 'sending Simulated PLCBUS Command' + GREY)
1161:debugin(@simulation)
1162:dataIn(@simulation)
1163:return @returnParamArray
1164:end
1165:#### START SETTERS ####################################################################
1166:def initialize()
1167:super
1168:@returnParamArray=Array.new
1169:end
1170:####  END  SETTERS ####################################################################
1171:end

Links