PLCBUS
Appearance

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
- ThreadedRuby
- http://wiki.linuxmce.org/index.php/PLCBUScmd776
- http://wiki.linuxmce.org/index.php/PLCBUScmd373
- http://wiki.linuxmce.org/index.php/PLCBUScmd351
- http://wiki.linuxmce.org/index.php/PLCBUScmd355
- http://wiki.linuxmce.org/index.php/PLCBUScmd384
- http://wiki.linuxmce.org/index.php/PLCBUScmd760
- http://wiki.linuxmce.org/index.php/PLCBUScmd776
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