PLCBUS
From LinuxMCE
Revision as of 00:23, 12 March 2008 by Hari (Talk | contribs) (→GSD/Ruby Code: added actual template)
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
- http://x10-hk.com/store/product_info.php?products_id=165
- http://www.elekhomica.nl/product_info.php?cPath=135&products_id=757
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