def __init__(self): pg.init() pg.display.set_caption("Platformer") self.running = True self.win = pg.display.set_mode((WIDTH, HEIGHT)) self.platforms = Platforms() self.player = Player() self.bgEnts = BgEnts() self.clock = pg.time.Clock() self.clock.tick()
def init(p1_actions): global player for i in range(height //100): for j in range(width // 100): plat = Platforms(random.randint(5, (width - 50) // 10) *10, 120 * i, 70, 40) platforms.add(plat) player = Player((platforms.sprites()))[-1].rect.centerx, platforms.sprites()[-1].rect.centery-300, p1_actions, sprite_list.add(player)
def init(): for i in range(height // 100): for j in range(width // 420): plat = Platforms( (random.randint(5, (width - 50) // 10) * 10, 120 * i), 'images/grassHalf.png', 70, 40) platforms.add(plat)
def __init__(self, system_settings, websocket, snmp_websocket, **kwargs): super(SleepyMeshBase, self).__init__(**kwargs) if 'last_syncs' not in self._defaults: self._defaults.update({'last_syncs': list()}) # Internal Members # self._mesh_awake = True self._sync_type = 'timeout' self._save_in_progress = False self._sync_average = None self._delay_average = None # Instances # # TODO: Eliminate as many dependencies as possible self.system_settings = system_settings self.websocket = websocket self.snmp_websocket = snmp_websocket self.modbus_server = ModbusServer() self.snmp_server = SNMPTrapServer(self) self.update_interfaces = UpdateInterfaces(self) self.update_in_progress = self.update_interfaces.update_in_progress self.bridge = Bridge(self.system_settings) self.uploader = Uploader(self) self.nodes = Nodes(self.system_settings) self.platforms = Platforms(self.nodes) self.networks = Networks(self) self.error = BaseError(self.system_settings) if self.system_settings.modbus_enable: system_settings_dict = self.system_settings.attr_dict() # LOGGER.debug('Modbus Attribute Dictionary: ' + str(system_settings_dict)) self.modbus_server.start(system_settings_dict) if self.system_settings.snmp_enable: self.snmp_server.start() # Overload Node Error Methods (SNMP Error Methods)# NodeError.send_snmp = self.snmp_server.send_snmp NodeError.clear_snmp = self.snmp_server.clear_snmp
def init(p1_actions): global player for i in range(height // 100): for j in range(width // 420): plat = Platforms((random.randint(5, (width - 50) // 10) * 10, 120 * i), './basic/images/grassHalf.png', 70, 40) platforms.add(plat) player = Player((platforms.sprites()[-1].rect.centerx, platforms.sprites()[-1].rect.centery-300), p1_actions) sprite_list.add(player)
class Main: def __init__(self): pg.init() pg.display.set_caption("Platformer") self.running = True self.win = pg.display.set_mode((WIDTH, HEIGHT)) self.platforms = Platforms() self.player = Player() self.bgEnts = BgEnts() self.clock = pg.time.Clock() self.clock.tick() # For key press and close button functionality def check_events(self): for event in pg.event.get(): if event.type == pg.QUIT: self.running = False # Update things def update(self): self.bgEnts.update() self.player.update(self.platforms.rects) # Draw things def render(self): self.win.fill(BLACK) self.bgEnts.render() self.platforms.render(self.win, self.player) self.player.render(self.win) pg.display.update() # The main loop def loop(self): while self.running: self.check_events() self.update() self.render() physics.dt = self.clock.tick(FPS) / 1000 pg.quit() sys.exit()
def init(p1_actions): # new above ******************************************************************** for i in range(height // 100): for j in range(width // 420): plat = Platforms( (random.randint(5, (width - 50) // 10) * 10, 120 * i), 'images/grassHalf.png', 70, 40) platforms.add(plat) # new follows ************************************************* player = Player((platforms.sprites()[-1].rect.centerx, platforms.sprites()[-1].rect.centery - 300), p1_actions) sprite_list.add(player)
def new_game(self, game_level = 1): running = True platforms = [] for i in Screen.PLATFORM_LOCATIONS[(game_level-1) % 5]: platforms.append(Platforms(self.surface, *i)) platform_rects = [i.rect for i in platforms] player = Player(self.surface) collectables = Collectables(self.surface) ship = Ship(self.surface) enemies = [] self.reset(running, player, ship, platforms, platform_rects, enemies, collectables, game_level) if game_level == 1 : music.load(Screen.GAME_MUSIC) music.play(-1)
class JumpGame(Animation): def getplatforms(num,L,L2,width1,width2,height1,height2): #beginning platforms count,ok,newlist = 0,True,L2 while count < num: check = Platforms(random.randint(width1,width2),\ random.randint(height1,height2)) for platform in L: #go through every platform to check overlap if platform.checkoverlap(check.x,check.y): ok = False break #check other list to check overlap if ok == True: for platform in L2: if platform.checkoverlap(check.x,check.y): ok = False break #if no overlaps, add platform to list if ok: newlist.append(check) count+=1 ok = True #reset, increment by one return newlist def latergetplatforms(L,L2,width1,width2,height): #add platforms during the game count,ok,newlist = 0,True,L2 while count < 1: check = Platforms(random.randint(width1,width2),\ height) #check platforms in list for overlap for platform in L: if platform.checkoverlap(check.x,check.y): ok = False break if ok == True: #check platforms in other list for overlap for platform in L2: if platform.checkoverlap(check.x,check.y): ok = False break if ok: newlist.append(check) count+=1 ok = True #increment by one, reset return newlist def init(self): self.mode = "mainmenu" self.module = Module(self.width//2,self.height//2+10) self.essentialplatforms = [Platforms(self.width//2,self.height-20),\ Platforms(random.randint(0,350),self.height-80),\ Platforms(random.randint(0,350),self.height-140),\ Platforms(random.randint(0,350),self.height-200),\ Platforms(random.randint(0,350),self.height-260),\ Platforms(random.randint(0,350),self.height-320),\ Platforms(random.randint(0,350),self.height-380)] #essential platforms for prevention of getting "stuck" self.platforms = [] self.platforms = JumpGame.getplatforms(10,self.essentialplatforms,\ self.platforms,0,350,20,370) #additional platforms scattered around. self.aliens = [] self.projectiles = [] self.enemyaircraft = [] self.enemyprojectiles = [] self.powerups = [] self.startmodule = MainMenuModule(self.width//2+self.width//4+7,\ self.height//2+50) self.startplatform = Platforms(self.width//2+self.width//4-10,\ self.height - 30) self.instructionsmodule = MainMenuModule(self.width//10,105) self.instructionsalien = Alien(self.width//18,160) self.instructionsaircraft = EnemyAircraft(self.width//15+30,100) self.ground = 350 self.maxplatforms = 10 self.maxaliens = 2 self.maxenemyaircrafts = 1 #max is for overload prevention self.maxenemyprojectiles = 7 self.maxpowerups = 2 self.deployable = True self.hitcount = 5 self.aircrafthit = False self.movejump = True self.storage = None self.background = \ PhotoImage(\ file="/Users/Edward/Desktop/TermProject/Images/background1.gif") #https://media.giphy.com/media/8xK1CusSFTI0U/giphy.gif self.score = 0 self.once = False #for updating aliens self.gameover = False self.fuel = False self.fuelscore = None self.protection = False self.protectionscore = None self.fuelprotection = False #for when on fuel self.donespeed = None self.extrajumps = 3 self.extrajumpscore = 0 self.canshoot = True self.canshoottime = 0 self.oldcanshoottime = 0 self.ringcolor = "gold" self.dx = 0 self.dy = -10 ###RACE self.computer = Computer(self.width//2,self.height//2+10) self.currentplatform = None self.targetplatform = None self.oldtargetplatform = None self.movement = None self.first = False self.stopmoving = False self.racescore = 0 self.youwin = False self.youlose = False self.finishline = True self.finish = [] #################################### # mode dispatcher #################################### #directory for all mousePressed, keyPressed, timerFired, and reDrawAlls def mousePressed(self,event): if (self.mode == "mainmenu"): JumpGame.mainmenuMousePressed(self,event) elif (self.mode == "spacejump"): JumpGame.spacejumpMousePressed(self,event) elif (self.mode == "instructions"): JumpGame.instructionsMousePressed(self,event) elif (self.mode == "instructionstwo"): JumpGame.instructionstwoMousePressed(self,event) elif(self.mode == "training"): JumpGame.trainingMousePressed(self,event) elif(self.mode == "race"): JumpGame.raceMousePressed(self,event) def keyPressed(self,event): if (self.mode == "mainmenu"): JumpGame.mainmenuKeyPressed(self,event) elif (self.mode == "spacejump"): JumpGame.spacejumpKeyPressed(self,event) elif (self.mode == "instructions"): JumpGame.instructionsKeyPressed(self,event) elif (self.mode == "instructions2:"): JumpGame.instructionstwoKeyPressed(self,event) elif (self.mode == "training"): JumpGame.trainingKeyPressed(self,event) elif(self.mode=="race"): JumpGame.raceKeyPressed(self,event) def timerFired(self): if (self.mode == "mainmenu"): JumpGame.mainmenuTimerFired(self) elif (self.mode == "spacejump"): JumpGame.spacejumpTimerFired(self) elif (self.mode == "instructions"): JumpGame.instructionsTimerFired(self) elif (self.mode == "instructionstwo"): JumpGame.instructionstwoTimerFired(self) elif (self.mode == "training"): JumpGame.trainingTimerFired(self) elif (self.mode == "race"): JumpGame.raceTimerFired(self) def redrawAll(self): if (self.mode == "mainmenu"): JumpGame.mainmenuRedrawAll(self) elif (self.mode == "spacejump"): JumpGame.spacejumpRedrawAll(self) elif (self.mode == "instructions"): JumpGame.instructionsRedrawAll(self) elif (self.mode == "instructionstwo"): JumpGame.instructionstwoRedrawAll(self) elif (self.mode == "training"): JumpGame.trainingRedrawAll(self) elif (self.mode == "race"): JumpGame.raceRedrawAll(self) ###### main menu ###### def mainmenuMousePressed(self,event): # if click is in range, then change mode. if event.x > self.width//2-30 and event.x < self.width//2+30 and\ event.y > self.height//2-15 and event.y < self.height//2+15: self.mode = "spacejump" elif event.x > self.width//2-45 and event.x < self.width//2+45 and\ event.y>self.height//2+30 and event.y<self.height//2+60: self.mode = "instructions" elif event.x >= self.width//2-40 and event.x <= self.width//2+40 and\ event.y>=self.height//2+75 and event.y<=self.height//2+105: self.mode = "training" elif event.x >= self.width//2-30 and event.x <= self.width//2+30 and\ event.y>=self.height//2+120 and event.y<=self.height//2+150: self.mode = "race" def mainmenuKeyPressed(self,event): pass def mainmenuTimerFired(self): #for decoration, aesthetics if self.startplatform.collide(self.startmodule.x,self.startmodule.x\ +self.startmodule.width,self.startmodule.y+self.startmodule.height): self.startmodule.jump(self.startmodule.y+self.startmodule.height) self.startmodule.update(self.width,self.height) def mainmenuRedrawAll(self): self.canvas.create_image(0,0,anchor="nw",image=self.background) self.canvas.create_text(self.width//2,75,text="Space Jump",font=\ "Arial 54",fill="white") self.canvas.create_rectangle(self.width//2-30,self.height//2-15,\ self.width//2+30,self.height//2+15,fill="brown") self.canvas.create_text(self.width//2,self.height//2,text="Play") self.canvas.create_rectangle(self.width//2-45,self.height//2+30,\ self.width//2+45,self.height//2+60,fill="brown") self.canvas.create_text(self.width//2,self.height//2+45,text=\ "Instructions") self.canvas.create_rectangle(self.width//2-40,self.height//2+75,\ self.width//2+40,self.height//2+105,fill="brown") self.canvas.create_text(self.width//2,self.height//2+90,\ text="Training") self.canvas.create_rectangle(self.width//2-30,self.height//2+120,\ self.width//2+30,self.height//2+150,fill="brown") self.canvas.create_text(self.width//2,self.height//2+135,\ text="Race") self.startplatform.draw(self.canvas) self.startmodule.draw(self.canvas) ####### SPACE JUMP ########## def spacejumpMousePressed(self,event): #calculate slope x1 = event.x y1 = event.y x2 = self.module.x+self.module.width//4+8 y2 = self.module.y-10 #difference yields general direction dx = (x1-x2)/20 #dy always makes bullet go up dy = -10 if y1 < self.module.y - 10: self.dx = dx self.dy = dy def spacejumpKeyPressed(self,event): if event.keysym == "Left": if self.module.speed > 0: self.module.speed = -1 if self.module.speed >= -self.module.maxspeed: self.module.speed -= 2 #increase speed left elif event.keysym == "Right": if self.module.speed < 0: self.module.speed = 1 if self.module.speed <= self.module.maxspeed: self.module.speed += 2 #increase speed right elif event.keysym == "Up": if not self.gameover and self.canshoot: self.projectiles.append(Projectile(self.module.x+\ self.module.width//4+8,self.module.y-10,self.dx,\ self.dy)) self.canshoot = False self.oldcanshoottime = 0 self.canshoottime = 0 elif event.keysym == "Down": if self.module.upspeed > 0 and self.extrajumps > 0: self.module.jump(self.module.y) self.extrajumps -= 1 if event.keysym == "b": JumpGame.init(self) def spacejumpTimerFired(self): if self.module.y+self.module.height>=self.height+12: self.gameover = True self.once = False if not self.gameover: #determine when fuel and protection end: if self.fuel and self.score-self.fuelscore >= 450: self.fuel = False self.ringcolor = "white" self.donespeed = self.module.upspeed if self.fuelprotection and not self.fuel and self.module.upspeed>=0: self.fuelprotection = False self.ringcolor = "gold" if self.protection and self.score-self.protectionscore>=1500: self.ringcolor="white" if self.protection and self.score-self.protectionscore>=2000: self.protection = False self.ringcolor = "gold" negatupspeed = -self.module.upspeed #increment speed for everything #check for collide in essential platforms for eplatform in self.essentialplatforms: if eplatform.collide(self.module.x,self.module.x+\ self.module.width,self.module.y+self.module.height): #only jump if the module is moving downwards if self.module.upspeed>0: self.module.jump(self.module.y+self.module.height) #remove essential platforms if they move off the screen if eplatform.y >= self.height + 15: self.essentialplatforms.remove(eplatform) #add back new one no matter what self.essentialplatforms = JumpGame.latergetplatforms(\ self.platforms,self.essentialplatforms,0,350,-15) #check for collide in regular platforms for platform in self.platforms: if platform.collide(self.module.x,self.module.x+\ self.module.width,self.module.y+self.module.height): if self.module.upspeed > 0: self.module.jump(self.module.y+self.module.height) #if platform moves off the screen, remove it from the list if platform.y >= self.height-15: self.platforms.remove(platform) if len(self.platforms) < self.maxplatforms: #only add if length of list less than max. self.platforms = JumpGame.latergetplatforms(\ self.essentialplatforms,self.platforms\ ,0,350,-15) #if module is passed the halfway mark if self.module.tempy<=self.height//2 and self.module.upspeed<0: #everything on the screen except for module moves accordingly #to the increment negatupspeed for eplatform in self.essentialplatforms: eplatform.y += negatupspeed self.score+=1 for platform in self.platforms: platform.y += negatupspeed for powerup in self.powerups: powerup.y += negatupspeed if powerup.y+powerup.height >= self.height: self.powerups.remove(powerup) #remove powerup from list if off the screen if self.aliens != [] and not self.once: for alien in self.aliens: alien.y += negatupspeed if alien.y >= self.height: self.aliens.remove(alien) #remove alien if off screen self.once = True #regarding aliens, jump on aliens/die from alien for alien in self.aliens: if alien.topcollide(self.module.x,self.module.x+self.module.\ width,self.module.y) and not self.protection and not \ self.fuelprotection: #hit it from the bottom self.gameover = True if alien.bottomcollide(self.module.x,self.module.x+self.module.\ width,self.module.y+self.module.height): if self.module.upspeed > 0: self.module.jump(self.module.y+self.module.height) self.aliens.remove(alien) #hit it from the top #projectiles for projectile in self.projectiles: #update projectile coordinates projectile.x += projectile.dx projectile.y += projectile.dy if projectile.y <= 0 or projectile.x <= 0 or \ projectile.x >= self.width: #remove projectiles if off the screen self.projectiles.remove(projectile) #if bullet hits alien, remove bullet and remove alien. for projectile in self.projectiles: for alien in self.aliens: if alien.topcollide(projectile.x-projectile.r,projectile.x\ +projectile.r,projectile.y-projectile.r): self.aliens.remove(alien) self.projectiles.remove(projectile) #if bullet hits aircraft, remove bullet for projectile in self.projectiles: if self.enemyaircraft != []: if self.enemyaircraft[0].collide(projectile.x-\ projectile.r,projectile.x+projectile.r,\ projectile.y-projectile.r): self.hitcount -= 1 #5 hits to kill aircraft self.aircrafthit = True self.projectiles.remove(projectile) if self.hitcount == 0: self.enemyaircraft.pop() self.hitcount = 5 #powerups for powerup in self.powerups: if powerup.collide(self.module.x,self.module.x+\ self.module.width,self.module.y) or powerup.collide(\ self.module.x,self.module.x+self.module.width,\ self.module.y + self.module.height) and not \ self.fuelprotection: self.powerups.remove(powerup) #if collide with module, remove powerup, #powerup effect in place. self.ringcolor="gold" if powerup.kind == "fuel": self.fuelscore = self.score self.fuelprotection = True self.fuel = True if powerup.kind == "protection": self.protectionscore = self.score self.protection = True #enemy aircraft and it's projectiles if self.enemyaircraft != []: #enemy aircraft moves according to the module's coordinates. if abs(self.enemyaircraft[0].x+self.enemyaircraft[0].width//2\ -self.module.x+self.module.width//2) < 3: self.enemyaircraft[0].dx = 0 elif self.enemyaircraft[0].x+self.enemyaircraft[0].width//2<\ self.module.x+self.module.width//2: self.enemyaircraft[0].dx = 5 elif self.enemyaircraft[0].x+self.enemyaircraft[0].width//2>=\ self.module.x+self.module.width//2: self.enemyaircraft[0].dx = -5 self.enemyaircraft[0].x += self.enemyaircraft[0].dx self.enemyaircraft[0].adjust(self.width) if self.enemyaircraft != [] and\ len(self.enemyprojectiles) < self.maxenemyprojectiles and \ random.randint(1,15)==6: #shoots bullets at random times self.enemyprojectiles.append(EnemyProjectile(\ self.enemyaircraft[0].x+self.enemyaircraft[0].width//2,\ self.enemyaircraft[0].y+self.enemyaircraft[0].height)) #eprojectile collide for eprojectile in self.enemyprojectiles: eprojectile.y += eprojectile.dy if self.module.collide(eprojectile.x,eprojectile.x+\ eprojectile.width,eprojectile.y+eprojectile.height) and \ not self.protection and not self.fuelprotection: self.enemyprojectiles.remove(eprojectile) #if bullet hits you, game over. self.gameover = True if eprojectile.y >= self.height: self.enemyprojectiles.remove(eprojectile) #HIGHER YOU GO, MORE EXTRA JUMPS if self.score - self.extrajumpscore >= 3000: self.extrajumps += 1 self.extrajumpscore = self.score #ADD aliens if self.score > 300 and len(self.aliens) < self.maxaliens and\ random.randint(1,50) == 5: self.aliens.append(Alien(random.randint(0,self.width-30),\ -15)) #CONTROL PROJECTILE SPAM self.module.update(self.width,self.height,self.fuel,\ self.fuelprotection) self.canshoottime += 1 if self.canshoottime - self.oldcanshoottime >= 4: self.canshoot = True #ADD ENEMY AIRCRAFT if self.score > 300 and len(self.enemyaircraft) <\ self.maxenemyaircrafts and random.randint(1,250)==24: self.enemyaircraft.append(EnemyAircraft(10,10)) #self.deployable = False #ADD POWERUP if self.score > 300 and len(self.powerups) <\ self.maxpowerups and random.randint(1,100)==2: kind = random.randint(1,10) #randomly select kind of powerup #some being more frequent than others. if kind > 3: self.powerups.append(Protection(random.randint(0,350),-15,\ "protection")) elif kind <=3: self.powerups.append(Fuel(random.randint(0,350),-15,"fuel")) #for determining how many platforms will be on the screen at a #certain score. In other words, determines difficulty if self.score > 1000 and self.score < 2000: self.maxplatforms = 9 if self.score >=2000 and self.score <4000: self.maxplatforms = 8 if self.score >= 4000 and self.score<5000: self.maxplatforms = 7 if self.score>=5000 and self.score<6000: self.maxplatforms=6 if self.score>=6000 and self.score<7000: self.maxplatforms=5 if self.score>=7000: self.maxplatforms=4 def spacejumpRedrawAll(self): #draws everything self.canvas.create_rectangle(-5,-5,500,500,fill="black") self.canvas.create_image(0,0,anchor="nw",image=self.background) for eplatform in self.essentialplatforms: eplatform.draw(self.canvas) for platform in self.platforms: platform.draw(self.canvas) for powerup in self.powerups: powerup.draw(self.canvas) for alien in self.aliens: alien.draw(self.canvas) for projectile in self.projectiles: projectile.draw(self.canvas) if not self.aircrafthit: for aircraft in self.enemyaircraft: aircraft.draw(self.canvas) self.aircrafthit = False for eprojectile in self.enemyprojectiles: eprojectile.draw(self.canvas) if self.protection or self.fuelprotection: self.canvas.create_oval(self.module.x-15,self.module.y-15,\ self.module.x+self.module.width+15,self.module.y+\ self.module.height+15,outline=self.ringcolor,width=7) self.module.draw(self.canvas) self.canvas.create_rectangle(0,0,self.width+20,30,fill="black") self.canvas.create_line(0,27,self.width+20,27,fill="white",\ width = 3) self.canvas.create_text(5,5,anchor="nw",text="Score : "+\ str(self.score),fill="white") self.canvas.create_text(100,5,anchor="nw",text="Extra Jumps: " + \ str(self.extrajumps),fill="white") if self.gameover: self.canvas.create_image(self.module.x-5,self.module.y-55,\ anchor="nw",image=self.module.deadphoto) self.canvas.create_rectangle(self.width//2-100,30,self.width//2+\ 100,130,fill="black",outline="white",width=3) self.canvas.create_text(self.width//2,70,\ text="GAME OVER",fill="red",font="Arial 26") self.canvas.create_text(self.width//2,100,\ text="Press (b) to escape",fill="white",\ font="Arial 16") ### INSTRUCTIONS1 ### #just design stuff def instructionsMousePressed(self,event): if event.x>=340 and event.x<=385 and event.y>=360 and event.y<=385: self.mode="instructionstwo" def instructionsKeyPressed(self,event): if event.keysym == "b": JumpGame.init(self) def instructionsTimerFired(self): pass def instructionsRedrawAll(self): self.canvas.create_image(0,0,anchor="nw",image=self.background) self.canvas.create_text(10,10,anchor="nw",\ text="You're out in space \n\t and you've ran out of fuel......",\ fill="white",font="Arial 18") self.canvas.create_text(self.width//2,60,anchor="n",\ text="How to Play",fill="white",font="Arial 28") self.canvas.create_line(10,95,10,390,fill="white") self.canvas.create_line(10,95,390,95,fill="white") self.canvas.create_line(390,95,390,390,fill="white") self.canvas.create_line(10,390,390,390,fill="white") self.instructionsmodule.draw(self.canvas) self.canvas.create_text(self.width//4,105,anchor="nw",text=\ "That's you, press left and right arrow keys\nto set direction",\ fill="white") self.instructionsalien.draw(self.canvas) self.canvas.create_text(self.width//4,160,anchor="nw",text=\ "Avoid aliens at all costs,\nunless you are able to jump on them",\ fill="white") self.canvas.create_rectangle(self.width//14,240,self.width//14+50,255,\ fill="gray") self.canvas.create_text(self.width//4,240,anchor="nw",\ text="Jump on these to survive",fill="white") self.canvas.create_rectangle(self.width//10,290,self.width//10+25,290+\ 25,fill="cyan") self.canvas.create_oval(self.width//10,290,self.width//10+25,290+\ 25,fill="green") self.canvas.create_text(self.width//10+12,290+12,\ text="P") self.canvas.create_rectangle(self.width//10,320,self.width//10+25,320+\ 25,fill="brown") self.canvas.create_oval(self.width//10,320,self.width//10+25,320+\ 25,fill="green") self.canvas.create_text(self.width//10+12,320+12,\ text="F") self.canvas.create_text(self.width//4,300,text=\ "These are powerups. Try to get them.\nThey will help you \ stay alive\nor increase your score",\ anchor="nw",fill="white") self.canvas.create_rectangle(340,360,385,385,fill="white",\ outline="white") self.canvas.create_text(362,372,text="Next") ####INSTRUCTIONS TWO#### #just design stuff def instructionstwoMousePressed(self,event): if event.x>=10 and event.x<=55 and event.y>=355 and event.y<=390: self.mode = "instructions" if event.x>=340 and event.x<=385 and event.y>=360 and event.y<=385: JumpGame.init(self) def instructionstwpKeyPressed(self,event): if event.keysym == "b": JumpGame.init(self) def instructionstwoTimerFired(self): pass def instructionstwoRedrawAll(self): self.canvas.create_image(0,0,anchor="nw",image=self.background) self.canvas.create_text(10,10,anchor="nw",\ text="You're out in space \n\t and you've ran out of fuel......",\ fill="white",font="Arial 18") self.canvas.create_text(self.width//2,60,anchor="n",\ text="How to Play",fill="white",font="Arial 28") self.canvas.create_line(10,95,10,390,fill="white") self.canvas.create_line(10,95,390,95,fill="white") self.canvas.create_line(390,95,390,390,fill="white") self.canvas.create_line(10,390,390,390,fill="white") self.instructionsaircraft.draw(self.canvas) self.canvas.create_text(self.width//2,140,\ text="Be careful of the spaceships.\nThey will shoot at you.",\ fill = "white",anchor="nw") self.canvas.create_text(self.width//2+4,260,text=\ "To shoot, press the up key. You can control the\ \ndirection of your shot by clicking in\nthe direction you prefer.",\ fill="white") self.canvas.create_text(self.width//2+5,319,text=\ "Your score increases the higher you go.",fill="white",\ font="Arial 14") self.canvas.create_text(self.width//2+5,350,\ text="Good luck!",fill="white",font="Arial 20") self.canvas.create_rectangle(15,360,60,385,fill="white",\ outline="white") self.canvas.create_text(37,372,text="Back") self.canvas.create_rectangle(340,360,385,385,fill="white",\ outline="white") self.canvas.create_text(362,372,text="Menu") #### TRAINING MODE ####### def trainingMousePressed(self,event): pass def trainingKeyPressed(self,event): if event.keysym == "Left": if self.module.speed > 0: self.module.speed = -1 if self.module.speed >= -self.module.maxspeed: self.module.speed -= 2 elif event.keysym == "Right": if self.module.speed < 0: self.module.speed = 1 if self.module.speed <= self.module.maxspeed: self.module.speed += 2 elif event.keysym == "Down": if self.module.upspeed > 0 and self.extrajumps > 0: self.module.jump(self.module.y) self.extrajumps -= 1 if event.keysym == "b": JumpGame.init(self) def trainingTimerFired(self): if self.module.y+self.module.height>=self.height+12: #if module goes below the screen, game over self.gameover = True self.once = False if not self.gameover: negatupspeed = -self.module.upspeed #increment for everything for eplatform in self.essentialplatforms: #check collision if eplatform.collide(self.module.x,self.module.x+\ self.module.width,self.module.y+self.module.height): if self.module.upspeed>0: self.module.jump(self.module.y+self.module.height) if eplatform.y >= self.height + 15: self.essentialplatforms.remove(eplatform) self.essentialplatforms = JumpGame.latergetplatforms(\ self.platforms,self.essentialplatforms,0,350,-15) for platform in self.platforms: #check collision if platform.collide(self.module.x,self.module.x+\ self.module.width,self.module.y+self.module.height): if self.module.upspeed > 0: self.module.jump(self.module.y+self.module.height) if platform.y >= self.height+15: #if platform below screen, remove platform self.platforms.remove(platform) if len(self.platforms) < self.maxplatforms: self.platforms = JumpGame.latergetplatforms(\ self.essentialplatforms,self.platforms\ ,0,350,-15) if self.module.tempy<=self.height//2 and self.module.upspeed<0: #add by increments for eplatform in self.essentialplatforms: eplatform.y += negatupspeed self.score += 1 for platform in self.platforms: platform.y += negatupspeed self.module.update(self.width,self.height,False,False) #set max platforms for difficulty according to score if self.score > 200 and self.score < 400: self.maxplatforms = 9 if self.score >=400 and self.score <600: self.maxplatforms = 7 if self.score >= 600 and self.score<800: self.maxplatforms = 5 if self.score>=800 and self.score<1000: self.maxplatforms=4 if self.score>=1000 and self.score<1200: self.maxplatforms=3 if self.score>=1200 and self.score<1400: self.maxplatforms=2 if self.score>=1400: self.maxplatforms=2 def trainingRedrawAll(self): self.canvas.create_rectangle(-5,-5,500,500,fill="black") self.canvas.create_image(0,0,anchor="nw",image=self.background) for platform in self.platforms: platform.draw(self.canvas) for eplatform in self.essentialplatforms: eplatform.draw(self.canvas) self.module.draw(self.canvas)#,self.gameover) self.canvas.create_rectangle(0,0,self.width+20,30,fill="black") self.canvas.create_line(0,27,self.width+20,27,fill="white",\ width = 3) self.canvas.create_text(5,5,anchor="nw",text="Score : "+\ str(self.score),fill="white") self.canvas.create_text(100,5,anchor="nw",text="Extra Jumps: " + \ str(self.extrajumps),fill="white") if self.gameover: self.canvas.create_image(self.module.x-5,self.module.y-55,\ anchor="nw",image=self.module.deadphoto) self.canvas.create_rectangle(self.width//2-100,30,self.width//2+\ 100,130,fill="black",outline="white",width=3) self.canvas.create_text(self.width//2,70,\ text="GAME OVER",fill="red",font="Arial 26") self.canvas.create_text(self.width//2,100,\ text="Press (b) to escape",fill="white",\ font="Arial 16") ###RACING MODE def raceMousePressed(self,event): pass def raceKeyPressed(self,event): if event.keysym == "Left": if self.module.speed > 0: self.module.speed = -1 if self.module.speed >= -self.module.maxspeed: self.module.speed -= 2 elif event.keysym == "Right": if self.module.speed < 0: self.module.speed = 1 if self.module.speed <= self.module.maxspeed: self.module.speed += 2 elif event.keysym == "Down": if self.module.upspeed > 0 and self.extrajumps > 0: self.module.jump(self.module.y) self.extrajumps -= 1 if event.keysym == "b": JumpGame.init(self) def raceTimerFired(self): if self.module.y+self.module.height>=self.height+12: self.gameover = True self.youlose = True if self.score >= 1800 and self.finishline: self.finish.append(Finish(0,-200,self.width)) self.finishline = False if self.finish != []: if self.module.y <= self.finish[0].y: self.gameover = True self.youwin = True elif self.computer.y <= self.finish[0].y: self.gameover = True self.youlose = True if self.racescore>=4000: self.gameover = True self.youlose = True if not self.gameover: negatupspeed = -self.module.upspeed #check collision for both computer and you for eplatform in self.essentialplatforms: if eplatform.collide(self.module.x,self.module.x+\ self.module.width,self.module.y+self.module.height): if self.module.upspeed>0: self.module.jump(self.module.y+self.module.height) #computer collision if eplatform.collide(self.computer.x,self.computer.x+\ self.computer.width,self.computer.y+self.computer.height): if self.computer.upspeed > 0: self.computer.jump(self.computer.y+self.computer.height) #set previously jumped on platform to currentplatform self.currentplatform=eplatform self.first,self.stopmoving = True,False #set speed to 0 to stabalize self.computer.speed = 0 for eplatform in self.essentialplatforms: #find next target in terms of platforms if eplatform.y<self.currentplatform.y and \ self.currentplatform.y-eplatform.y<=200: self.targetplatform = eplatform break #remove platforms if off screen if eplatform.y >= self.height + 15: self.essentialplatforms.remove(eplatform) self.essentialplatforms = JumpGame.latergetplatforms(\ self.platforms,self.essentialplatforms,0,350,-15) #check collide with regular platforms for yourself and computer for platform in self.platforms: if platform.collide(self.module.x,self.module.x+\ self.module.width,self.module.y+self.module.height): if self.module.upspeed > 0: self.module.jump(self.module.y+self.module.height) #computer collide if platform.collide(self.computer.x,self.computer.x+\ self.computer.width,self.computer.y+self.computer.height): if self.computer.upspeed > 0: self.computer.jump(self.computer.y+self.computer.height) #set platform jumped off of to currentplatform self.currentplatform = platform self.first,self.stopmoving = True,False #speed 0 to stabalize self.computer.speed = 0 for eplatform in self.essentialplatforms: if eplatform.y<self.currentplatform.y and \ self.currentplatform.y-eplatform.y<=200: #look for next target in terms of platforms. self.targetplatform = eplatform break #remove regular platform if off the screen if platform.y >= self.height+15: self.platforms.remove(platform) if len(self.platforms) < self.maxplatforms: self.platforms = JumpGame.latergetplatforms(\ self.essentialplatforms,self.platforms\ ,0,350,-15) #if computer is bottom off screen if self.computer.y>=self.height+self.width//2: if random.randint(1,10)<11: self.computer.y = self.height+160 self.computer.jump(self.computer.y+self.computer.height) for eplatform in self.essentialplatforms: #find target accordingly if eplatform.y<self.computer.y+self.computer.height and\ self.computer.y+self.computer.height-eplatform.y<=250: self.targetplatform = eplatform break if self.module.tempy<=self.height//2 and self.module.upspeed<0: #add increments accordingly if self.finish != []: self.finish[0].y += negatupspeed for eplatform in self.essentialplatforms: eplatform.y += negatupspeed self.score += 1 for platform in self.platforms: platform.y += negatupspeed self.computer.y += negatupspeed #If computer ahead, keep track of time with racescore if self.computer.y+self.computer.height<25: self.racescore += 5 if self.computer.upspeed<0: self.racescore += 4 #if computer in bounds, reset racescore to your score if self.computer.y<self.module.y and \ self.module.y-self.computer.y<225: self.racescore = self.score + 100 elif self.computer.y>self.module.y and \ self.computer.y-self.module.y<300: self.racescore = self.score - 100 if self.first: #determine distance between currentplatform and targetplatform if self.targetplatform != None and \ self.targetplatform.x > self.currentplatform.x: distance1 = self.targetplatform.x - self.currentplatform.x distance2 = (self.width-self.targetplatform.x)+\ self.currentplatform.x elif self.targetplatform != None and self.targetplatform.x <=\ self.currentplatform.x: distance1 = (self.width-self.currentplatform.x)+\ self.targetplatform.x distance2 = self.currentplatform.x-self.targetplatform.x if distance1 <= distance2: self.movement = "right" elif distance2 < distance1: self.movement = "left" if self.movement == "right" and not self.stopmoving: if self.computer.speed <= 0: self.computer.speed = 4 if self.computer.speed <= self.computer.maxspeed: self.computer.speed += 2 if self.movement == "left" and not self.stopmoving: if self.computer.speed > 0: self.computer.speed = -4 if self.computer.speed >= -self.module.maxspeed: self.computer.speed -= 2 if self.targetplatform != None and self.computer.x+\ self.computer.width//2>=self.targetplatform.x and\ self.computer.x+self.computer.width//2<=\ self.targetplatform.x+self.targetplatform.width\ and self.computer.y<=self.targetplatform.y: #set computer speed to 0 once directly on top of target #platform to ensure succesful landing self.computer.speed = 0 #update both self.computer.update(self.width,self.height,False,False) self.module.update(self.width,self.height,False,False) #set difficulty based off of how many platforms are on the screen if self.score > -1 and self.score < 300: self.maxplatforms = 9 if self.score >=300 and self.score <600: self.maxplatforms = 8 if self.score >= 600 and self.score<900: self.maxplatforms = 7 if self.score>=900 and self.score<1200: self.maxplatforms=6 if self.score>=1200 and self.score<1500: self.maxplatforms=5 if self.score>=1500: self.maxplatforms=4 def raceRedrawAll(self): self.canvas.create_rectangle(-5,-5,500,500,fill="black") self.canvas.create_image(0,0,anchor="nw",image=self.background) if self.finish != []: self.finish[0].draw(self.canvas) for platform in self.platforms: platform.draw(self.canvas) for eplatform in self.essentialplatforms: eplatform.draw(self.canvas) self.computer.draw(self.canvas) self.module.draw(self.canvas) self.canvas.create_rectangle(0,0,self.width+20,30,fill="black") self.canvas.create_line(0,27,self.width+20,27,fill="white",\ width = 3) self.canvas.create_text(5,5,anchor="nw",text="Score : "+\ str(self.score),fill="white") self.canvas.create_text(100,5,anchor="nw",text="Extra Jumps: " + \ str(self.extrajumps),fill="white") if self.gameover and self.youwin: self.canvas.create_rectangle(self.width//2-100,30,self.width//2+\ 100,130,fill="black",outline="white",width=3) self.canvas.create_text(self.width//2,70,\ text="YOU WIN",fill="green",font="Arial 26") self.canvas.create_text(self.width//2,100,\ text="Press (b) to escape",fill="white",\ font="Arial 16") if self.gameover and self.youlose: self.canvas.create_image(self.module.x-5,self.module.y-55,\ anchor="nw",image=self.module.deadphoto) self.canvas.create_rectangle(self.width//2-100,30,self.width//2+\ 100,130,fill="black",outline="white",width=3) self.canvas.create_text(self.width//2,70,\ text="YOU LOSE",fill="red",font="Arial 26") self.canvas.create_text(self.width//2,100,\ text="Press (b) to escape",fill="white",\ font="Arial 16")
def __init__(self, webroot=DEFAULT_WEB_ROOT, users={}, topic_replace_list=[], **kwargs): """ Creates a `VolttronCentralAgent` object to manage instances. Each instances that is registered must contain a running `VolttronCentralPlatform`. Through this conduit the `VolttronCentralAgent` is able to communicate securly and efficiently. :param config_path: :param kwargs: :return: """ _log.info("{} constructing...".format(self.__class__.__name__)) super(VolttronCentralAgent, self).__init__(enable_web=True, **kwargs) # Create default configuration to be used in case of problems in the # packaged agent configuration file. self._default_config = dict( webroot=os.path.abspath(webroot), users=users, topic_replace_list=topic_replace_list ) self.vip.config.set_default("config", self._default_config) # Start using config store. self.vip.config.subscribe(self._configure, actions=["NEW", "UPDATE"], pattern="config") # # # During the configuration update/new/delete action this will be # # updated to the current configuration. # self.runtime_config = None # # # Start using config store. # self.vip.config.set_default("config", config) # self.vip.config.subscribe(self.configure_main, # actions=['NEW', 'UPDATE', 'DELETE'], # pattern="config") # # # Use config store to update the settings of a platform's configuration. # self.vip.config.subscribe(self.configure_platforms, # actions=['NEW', 'UPDATE', 'DELETE'], # pattern="platforms/*") # # # mapping from the real topic into the replacement. # self.replaced_topic_map = {} # # # mapping from md5 hash of address to the actual connection to the # # remote instance. # self.vcp_connections = {} # # # Current sessions available to the # self.web_sessions = None # # # Platform health based upon device driver publishes # self.device_health = defaultdict(dict) # # # Used to hold scheduled reconnection event for vcp agents. # self._vcp_reconnect_event = None # # # the registered socket endpoints so we can send out management # # events to all the registered session. self._websocket_endpoints = set() self._platforms = Platforms(self) self._platform_scan_event = None # Sessions that have been authentication with the system. self._authenticated_sessions = None
class VolttronCentralAgent(Agent): """ Agent for managing many volttron instances from a central web ui. During the """ def __init__(self, webroot=DEFAULT_WEB_ROOT, users={}, topic_replace_list=[], **kwargs): """ Creates a `VolttronCentralAgent` object to manage instances. Each instances that is registered must contain a running `VolttronCentralPlatform`. Through this conduit the `VolttronCentralAgent` is able to communicate securly and efficiently. :param config_path: :param kwargs: :return: """ _log.info("{} constructing...".format(self.__class__.__name__)) super(VolttronCentralAgent, self).__init__(enable_web=True, **kwargs) # Create default configuration to be used in case of problems in the # packaged agent configuration file. self._default_config = dict( webroot=os.path.abspath(webroot), users=users, topic_replace_list=topic_replace_list ) self.vip.config.set_default("config", self._default_config) # Start using config store. self.vip.config.subscribe(self._configure, actions=["NEW", "UPDATE"], pattern="config") # # # During the configuration update/new/delete action this will be # # updated to the current configuration. # self.runtime_config = None # # # Start using config store. # self.vip.config.set_default("config", config) # self.vip.config.subscribe(self.configure_main, # actions=['NEW', 'UPDATE', 'DELETE'], # pattern="config") # # # Use config store to update the settings of a platform's configuration. # self.vip.config.subscribe(self.configure_platforms, # actions=['NEW', 'UPDATE', 'DELETE'], # pattern="platforms/*") # # # mapping from the real topic into the replacement. # self.replaced_topic_map = {} # # # mapping from md5 hash of address to the actual connection to the # # remote instance. # self.vcp_connections = {} # # # Current sessions available to the # self.web_sessions = None # # # Platform health based upon device driver publishes # self.device_health = defaultdict(dict) # # # Used to hold scheduled reconnection event for vcp agents. # self._vcp_reconnect_event = None # # # the registered socket endpoints so we can send out management # # events to all the registered session. self._websocket_endpoints = set() self._platforms = Platforms(self) self._platform_scan_event = None # Sessions that have been authentication with the system. self._authenticated_sessions = None def _configure(self, config_name, action, contents): """ The main configuration for volttron central. This is where validation will occur. Note this method is called: 1. When the agent first starts (with the params from packaged agent file) 2. When 'store' is called through the volttron-ctl config command line with 'config' as the name. Required Configuration: The volttron central requires a user mapping. :param config_name: :param action: :param contents: """ config = self._default_config.copy() config.update(contents) users = config.get("users", None) if self._authenticated_sessions: self._authenticated_sessions.clear() if users is None: users = {} _log.warn("No users are available for logging in!") # Unregister all routes for vc and then re-add down below. self.vip.web.unregister_all_routes() self._authenticated_sessions = SessionHandler(Authenticate(users)) self.vip.web.register_endpoint(r'/vc/jsonrpc', self.jsonrpc) self.vip.web.register_websocket(r'/vc/ws', self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) self.vip.web.register_path(r'^/vc/.*', config.get('webroot')) # Start scanning for new platforms connections as well as for # disconnects that happen. self._scan_platform_connect_disconnect() @staticmethod def _get_next_time_seconds(seconds=10): now = get_aware_utc_now() next_time = now + datetime.timedelta(seconds=seconds) return next_time def _handle_platform_connection(self, platform_vip_identity): _log.info("Handling new platform connection {}".format( platform_vip_identity)) platform = self._platforms.add_platform(platform_vip_identity) def _handle_platform_disconnect(self, platform_vip_identity): _log.warn("Handling disconnection of connection from identity: {}".format( platform_vip_identity )) # TODO send alert that there was a platform disconnect. self._platforms.disconnect_platform(platform_vip_identity) def _scan_platform_connect_disconnect(self): """ Scan the local bus for peers that start with 'vcp-'. Handle the connection and disconnection events here. """ if self._platform_scan_event is not None: # This won't hurt anything if we are canceling ourselves. self._platform_scan_event.cancel() # Identities of all platform agents that are connecting to us should # have an identity of platform.md5hash. connected_platforms = set([x for x in self.vip.peerlist().get(timeout=5) if x.startswith('vcp-')]) disconnected = self._platforms.get_platform_keys() - connected_platforms for vip_id in disconnected: self._handle_platform_disconnect(vip_id) not_known = connected_platforms - self._platforms.get_platform_keys() for vip_id in not_known: self._handle_platform_connection(vip_id) next_platform_scan = VolttronCentralAgent._get_next_time_seconds() # reschedule the next scan. self._platform_scan_event = self.core.schedule( next_platform_scan, self._scan_platform_connect_disconnect) def configure_platforms(self, config_name, action, contents): _log.debug('Platform configuration updated.') _log.debug('ACTION IS {}'.format(action)) _log.debug('CONTENT IS {}'.format(contents)) def open_authenticate_ws_endpoint(self, fromip, endpoint): """ Callback method from when websockets are opened. The endpoine must be '/' delimited with the second to last section being the session of a logged in user to volttron central itself. :param fromip: :param endpoint: A string representing the endpoint of the websocket. :return: """ _log.debug("OPENED ip: {} endpoint: {}".format(fromip, endpoint)) try: session = endpoint.split('/')[-2] except IndexError: _log.error("Malformed endpoint. Must be delimited by '/'") _log.error( 'Endpoint must have valid session in second to last position') return False if not self._authenticated_sessions.check_session(session, fromip): _log.error("Authentication error for session!") return False _log.debug('Websocket allowed.') self._websocket_endpoints.add(endpoint) return True def _ws_closed(self, endpoint): _log.debug("CLOSED endpoint: {}".format(endpoint)) try: self._websocket_endpoints.remove(endpoint) except KeyError: pass # This should never happen but protect against it anyways. def _ws_received(self, endpoint, message): _log.debug("RECEIVED endpoint: {} message: {}".format(endpoint, message)) @RPC.export def is_registered(self, address_hash=None, address=None): if address_hash is None and address is None: return False if address_hash is None: address_hash = PlatformHandler.address_hasher(address) return self._platforms.is_registered(address_hash) @RPC.export def get_publickey(self): """ RPC method allowing the caller to retrieve the publickey of this agent. This method is available for allowing :class:`VolttronCentralPlatform` agents to allow this agent to be able to connect to its instance. :return: The publickey of this volttron central agent. :rtype: str """ return self.core.publickey @RPC.export def unregister_platform(self, platform_uuid): _log.debug('unregister_platform') platform = self._registered_platforms.get(platform_uuid) if platform: connected = self._platform_connections.get(platform_uuid) if connected is not None: connected.call('unmanage') connected.kill() address = None for v in self._address_to_uuid.values(): if v == platform_uuid: address = v break if address: del self._address_to_uuid[address] del self._platform_connections[platform_uuid] del self._registered_platforms[platform_uuid] self._registered_platforms.sync() context = 'Unregistered platform {}'.format(platform_uuid) return {'status': 'SUCCESS', 'context': context} else: msg = 'Unable to unregistered platform {}'.format(platform_uuid) return {'error': {'code': UNABLE_TO_UNREGISTER_INSTANCE, 'message': msg}} def _to_jsonrpc_obj(self, jsonrpcstr): """ Convert data string into a JsonRpcData named tuple. :param object data: Either a string or a dictionary representing a json document. """ return jsonrpc.JsonRpcData.parse(jsonrpcstr) def jsonrpc(self, env, data): """ The main entry point for ^jsonrpc data This method will only accept rpcdata. The first time this method is called, per session, it must be using get_authorization. That will return a session token that must be included in every subsequent request. The session is tied to the ip address of the caller. :param object env: Environment dictionary for the request. :param object data: The JSON-RPC 2.0 method to call. :return object: An JSON-RPC 2.0 response. """ if env['REQUEST_METHOD'].upper() != 'POST': return jsonrpc.json_error('NA', INVALID_REQUEST, 'Invalid request method, only POST allowed' ) try: rpcdata = self._to_jsonrpc_obj(data) _log.info('rpc method: {}'.format(rpcdata.method)) if rpcdata.method == 'get_authorization': args = {'username': rpcdata.params['username'], 'password': rpcdata.params['password'], 'ip': env['REMOTE_ADDR']} sess = self._authenticated_sessions.authenticate(**args) if not sess: _log.info('Invalid username/password for {}'.format( rpcdata.params['username'])) return jsonrpc.json_error( rpcdata.id, UNAUTHORIZED, "Invalid username/password specified.") _log.info('Session created for {}'.format( rpcdata.params['username'])) self.vip.web.register_websocket( "/vc/ws/{}/management".format(sess), self.open_authenticate_ws_endpoint, self._ws_closed, self._received_data) _log.info('Session created for {}'.format( rpcdata.params['username'])) gevent.sleep(1) return jsonrpc.json_result(rpcdata.id, sess) token = rpcdata.authorization ip = env['REMOTE_ADDR'] _log.debug('REMOTE_ADDR: {}'.format(ip)) session_user = self._authenticated_sessions.check_session(token, ip) _log.debug('SESSION_USER IS: {}'.format(session_user)) if not session_user: _log.debug("Session Check Failed for Token: {}".format(token)) return jsonrpc.json_error(rpcdata.id, UNAUTHORIZED, "Invalid authentication token") _log.debug('RPC METHOD IS: {}'.format(rpcdata.method)) # Route any other method that isn't result_or_error = self._route_request(session_user, rpcdata.id, rpcdata.method, rpcdata.params) except AssertionError: return jsonrpc.json_error( 'NA', INVALID_REQUEST, 'Invalid rpc data {}'.format(data)) except Unreachable: return jsonrpc.json_error( rpcdata.id, UNAVAILABLE_PLATFORM, "Couldn't reach platform with method {} params: {}".format( rpcdata.method, rpcdata.params)) except Exception as e: return jsonrpc.json_error( 'NA', UNHANDLED_EXCEPTION, e ) return self._get_jsonrpc_response(rpcdata.id, result_or_error) def _get_jsonrpc_response(self, id, result_or_error): """ Wrap the response in either a json-rpc error or result. :param id: :param result_or_error: :return: """ if isinstance(result_or_error, dict): if 'jsonrpc' in result_or_error: return result_or_error if result_or_error is not None and isinstance(result_or_error, dict): if 'error' in result_or_error: error = result_or_error['error'] _log.debug("RPC RESPONSE ERROR: {}".format(error)) return jsonrpc.json_error(id, error['code'], error['message']) return jsonrpc.json_result(id, result_or_error) def _get_agents(self, instance_uuid, groups): """ Retrieve the list of agents on a specific platform. :param instance_uuid: :param groups: :return: """ _log.debug('_get_agents') connected_to_pa = self._platform_connections[instance_uuid] agents = connected_to_pa.agent.vip.rpc.call( 'platform.agent', 'list_agents').get(timeout=30) for a in agents: if 'admin' in groups: if "platformagent" in a['name'] or \ "volttroncentral" in a['name']: a['vc_can_start'] = False a['vc_can_stop'] = False a['vc_can_restart'] = True else: a['vc_can_start'] = True a['vc_can_stop'] = True a['vc_can_restart'] = True else: # Handle the permissions that are not admin. a['vc_can_start'] = False a['vc_can_stop'] = False a['vc_can_restart'] = False _log.debug('Agents returned: {}'.format(agents)) return agents def _setupexternal(self): _log.debug(self.vip.ping('', "PING ROUTER?").get(timeout=3)) def _configure_agent(self, endpoint, message): _log.debug('Configure agent: {} message: {}'.format(endpoint, message)) def _received_data(self, endpoint, message): print('Received from endpoint {} message: {}'.format(endpoint, message)) self.vip.web.send(endpoint, message) def set_setting(self, session_user, params): """ Sets or removes a setting from the config store. If the value is None then the item will be removed from the store. If there is an error in saving the value then a jsonrpc.json_error object is returned. :param session_user: Unused :param params: Dictionary that must contain 'key' and 'value' keys. :return: A 'SUCCESS' string or a jsonrpc.json_error object. """ if 'key' not in params or not params['key']: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') if 'value' not in params: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') config_key = "settings/{}".format(params['key']) value = params['value'] if value is None: try: self.vip.config.delete(config_key) except KeyError: pass else: # We handle empt string here because the config store doesn't allow # empty strings to be set as a config store. I wasn't able to # trap the ValueError that is raised on the server side. if value == "": return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid value set (empty string?)') self.vip.config.set(config_key, value) return 'SUCCESS' def get_setting(self, session_user, params): """ Retrieve a value from the passed setting key. The params object must contain a "key" to return from the settings store. :param session_user: Unused :param params: Dictionary that must contain a 'key' key. :return: The value or a jsonrpc error object. """ config_key = "settings/{}".format(params['key']) try: value = self.vip.config.get(config_key) except KeyError: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid key specified') else: return value def get_setting_keys(self, session_user, params): """ Returns a list of all of the settings keys so the caller can know what settings to request. :param session_user: Unused :param params: Unused :return: A list of settings available to the caller. """ prefix = "settings/" keys = [x[len(prefix):] for x in self.vip.config.list() if x.startswith(prefix)] return keys or [] def _handle_bacnet_props(self, session_user, params): platform_uuid = params.pop('platform_uuid') id = params.pop('message_id') _log.debug('Handling bacnet_props platform: {}'.format(platform_uuid)) configure_topic = "{}/configure".format(session_user['token']) ws_socket_topic = "/vc/ws/{}".format(configure_topic) if configure_topic not in self._websocket_endpoints: self.vip.web.register_websocket(ws_socket_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) def start_sending_props(): response_topic = "configure/{}".format(session_user['token']) # Two ways we could have handled this is to pop the identity off # of the params and then passed both the identity and the response # topic. Or what I chose to do and to put the argument in a # copy of the params. cp = params.copy() cp['publish_topic'] = response_topic cp['device_id'] = int(cp['device_id']) platform = self._platforms.get_platform(platform_uuid) _log.debug('PARAMS: {}'.format(cp)) platform.call("publish_bacnet_props", **cp) gevent.spawn_later(2, start_sending_props) def _handle_bacnet_scan(self, session_user, params): platform_uuid = params.pop('platform_uuid') id = params.pop('message_id') _log.debug('Handling bacnet_scan platform: {}'.format(platform_uuid)) if not self._platforms.is_registered(platform_uuid): return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) scan_length = params.pop('scan_length', 5) try: scan_length = float(scan_length) params['scan_length'] = scan_length platform = self._platforms.get_platform(platform_uuid) iam_topic = "{}/iam".format(session_user['token']) ws_socket_topic = "/vc/ws/{}".format(iam_topic) self.vip.web.register_websocket(ws_socket_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) def start_scan(): # We want the datatype (iam) to be second in the response so # we need to reposition the iam and the session id to the topic # that is passed to the rpc function on vcp iam_session_topic = "iam/{}".format(session_user['token']) platform.call("start_bacnet_scan", iam_session_topic, **params) def close_socket(): _log.debug('Closing bacnet scan for {}'.format( platform_uuid)) gevent.spawn_later(2, self.vip.web.unregister_websocket, iam_session_topic) gevent.spawn_later(scan_length, close_socket) # By starting the scan a second later we allow the websocket # client to subscribe to the newly available endpoint. gevent.spawn_later(2, start_scan) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) except KeyError: return jsonrpc.json_error(id, UNAUTHORIZED, "Invalid user session token") def _enable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error enable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to enable setup mode") auth_file = AuthFile() entries = auth_file.find_by_credentials(".*") if len(entries) > 0: return "SUCCESS" entry = AuthEntry(credentials="/.*/", comments="Un-Authenticated connections allowed here", user_id="unknown") auth_file.add(entry) return "SUCCESS" def _disable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error disable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to disable setup mode") auth_file = AuthFile() auth_file.remove_by_credentials("/.*/") return "SUCCESS" def _handle_management_endpoint(self, session_user, params): ws_topic = "/vc/ws/{}/management".format(session_user.get('token')) self.vip.web.register_websocket(ws_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) return ws_topic def send_management_message(self, type, data={}): """ Send a message to any socket that has connected to the management socket. The payload sent to the client is like the following:: { "type": "UPDATE_DEVICE_STATUS", "data": "this is data that was passed" } :param type: A string defining a unique type for sending to the websockets. :param data: An object that str can be called on. :type type: str :type data: serializable """ management_sockets = [s for s in self._websocket_endpoints if s.endswith("management")] # Nothing to send if we don't have any management sockets open. if len(management_sockets) <= 0: return if data is None: data = {} payload = dict( type=type, data=str(data) ) payload = jsonapi.dumps(payload) for s in management_sockets: self.vip.web.send(s, payload) def _route_request(self, session_user, id, method, params): """ Handle the methods volttron central can or pass off to platforms. :param session_user: The authenticated user's session info. :param id: JSON-RPC id field. :param method: :param params: :return: """ _log.debug( 'inside _route_request {}, {}, {}'.format(id, method, params)) def err(message, code=METHOD_NOT_FOUND): return {'error': {'code': code, 'message': message}} self.send_management_message(method) method_split = method.split('.') # The last part of the jsonrpc method is the actual method to be called. method_check = method_split[-1] # These functions will be sent to a platform.agent on either this # instance or another. All of these functions have the same interface # and can be collected into a dictionary rather than an if tree. platform_methods = dict( # bacnet related start_bacnet_scan=self._handle_bacnet_scan, publish_bacnet_props=self._handle_bacnet_props, # config store related store_agent_config="store_agent_config", get_agent_config="get_agent_config", list_agent_configs="get_agent_config_list", # management related list_agents="get_agent_list", get_devices="get_devices", status_agents="status_agents" ) # These methods are specifically to be handled by the platform not any # agents on the platform that is why we have the length requirement. # # The jsonrpc method looks like the following # # platform.uuid.<dynamic entry>.method_on_vcp if method_check in platform_methods: platform_uuid = None if isinstance(params, dict): platform_uuid = params.pop('platform_uuid', None) if platform_uuid is None: if method_split[0] == 'platforms' and method_split[1] == 'uuid': platform_uuid = method_split[2] if not platform_uuid: return err("Invalid platform_uuid specified as parameter" .format(platform_uuid), INVALID_PARAMS) if not self._platforms.is_registered(platform_uuid): return err("Unknown or unavailable platform {} specified as " "parameter".format(platform_uuid), UNAVAILABLE_PLATFORM) try: _log.debug('Calling {} on platform {}'.format( method_check, platform_uuid )) class_method = platform_methods[method_check] platform = self._platforms.get_platform(platform_uuid) # Determine whether the method to call is on the current class # or on the platform object. if isinstance(class_method, basestring): method_ref = getattr(platform, class_method) else: method_ref = class_method # Put the platform_uuid in the params so it can be used # inside the method params['platform_uuid'] = platform_uuid except AttributeError or KeyError: return jsonrpc.json_error(id, INTERNAL_ERROR, "Attempted calling function " "{} was unavailable".format( class_method )) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform " "{}".format(platform_uuid)) else: # pass the id through the message_id parameter. if not params: params = dict(message_id=id) else: params['message_id'] = id # Methods will all have the signature # method(session, params) # return method_ref(session_user, params) vc_methods = dict( register_management_endpoint=self._handle_management_endpoint, list_platforms=self._platforms.get_platform_list, list_performance=self._platforms.get_performance_list, # Settings set_setting=self.set_setting, get_setting=self.get_setting, get_setting_keys=self.get_setting_keys, # Setup mode enable_setup_mode=self._enable_setup_mode, disable_setup_mode=self._disable_setup_mode ) if method in vc_methods: if not params: params = dict(message_id=id) else: params['message_id'] = id response = vc_methods[method](session_user, params) _log.debug("Response is {}".format(response)) return response # vc_methods[method](session_user, params) if method == 'register_instance': if isinstance(params, list): return self._register_instance(*params) else: return self._register_instance(**params) elif method == 'unregister_platform': return self.unregister_platform(params['instance_uuid']) elif 'historian' in method: has_platform_historian = PLATFORM_HISTORIAN in \ self.vip.peerlist().get(timeout=30) if not has_platform_historian: return err( 'The VOLTTRON Central platform historian is unavailable.', UNAVAILABLE_AGENT) _log.debug('Trapping platform.historian to vc.') _log.debug('has_platform_historian: {}'.format( has_platform_historian)) if 'historian.query' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'query', **params).get(timeout=30) elif 'historian.get_topic_list' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'get_topic_list').get(timeout=30) # This isn't known as a proper method on vc or a platform. if len(method_split) < 3: return err('Unknown method {}'.format(method)) if method_split[0] != 'platforms' or method_split[1] != 'uuid': return err('Invalid format for instance must start with ' 'platforms.uuid') instance_uuid = method_split[2] _log.debug('Instance uuid is: {}'.format(instance_uuid)) if not self._platforms.is_registered(instance_uuid): return err('Unknown platform {}'.format(instance_uuid)) platform_method = '.'.join(method_split[3:]) _log.debug("Platform method is: {}".format(platform_method)) platform = self._platforms.get_platform(instance_uuid) if not platform: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "cannot connect to platform." ) if platform_method.startswith('install'): if 'admin' not in session_user['groups']: return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to install agents") return platform.route_to_agent_method(id, platform_method, params) def _validate_config_params(self, config): """ Validate the configuration parameters of the default/updated parameters. This method will return a list of "problems" with the configuration. If there are no problems then an empty list is returned. :param config: Configuration parameters for the volttron central agent. :type config: dict :return: The problems if any, [] if no problems :rtype: list """ problems = [] webroot = config.get('webroot') if not webroot: problems.append('Invalid webroot in configuration.') elif not os.path.exists(webroot): problems.append( 'Webroot {} does not exist on machine'.format(webroot)) users = config.get('users') if not users: problems.append('A users node must be specified!') else: has_admin = False try: for user, item in users.items(): if 'password' not in item.keys(): problems.append('user {} must have a password!'.format( user)) elif not item['password']: problems.append('password for {} is blank!'.format( user )) if 'groups' not in item.keys(): problems.append('missing groups key for user {}'.format( user )) elif not isinstance(item['groups'], list): problems.append('groups must be a list of strings.') elif not item['groups']: problems.append( 'user {} must belong to at least one group.'.format( user)) # See if there is an adminstator present. if not has_admin and isinstance(item['groups'], list): has_admin = 'admin' in item['groups'] except AttributeError: problems.append('invalid user node.') if not has_admin: problems.append("One user must be in the admin group.") return problems
def __init__(self, config_path, **kwargs): """ Creates a `VolttronCentralAgent` object to manage instances. Each instances that is registered must contain a running `VolttronCentralPlatform`. Through this conduit the `VolttronCentralAgent` is able to communicate securly and efficiently. :param config_path: :param kwargs: :return: """ _log.info("{} constructing...".format(self.__class__.__name__)) super(VolttronCentralAgent, self).__init__(enable_web=True, **kwargs) # Load the configuration into a dictionary config = utils.load_config(config_path) # Required users users = config.get('users', None) # Expose the webroot property to be customized through the config # file. webroot = config.get('webroot', DEFAULT_WEB_ROOT) if webroot.endswith('/'): webroot = webroot[:-1] topic_replace_list = config.get('topic-replace-list', []) # Create default configuration to be used in case of problems in the # packaged agent configuration file. self.default_config = dict(webroot=os.path.abspath(webroot), users=users, topic_replace_list=topic_replace_list) # During the configuration update/new/delete action this will be # updated to the current configuration. self.runtime_config = None # Start using config store. self.vip.config.set_default("config", config) self.vip.config.subscribe(self.configure_main, actions=['NEW', 'UPDATE', 'DELETE'], pattern="config") # Use config store to update the settings of a platform's configuration. self.vip.config.subscribe(self.configure_platforms, actions=['NEW', 'UPDATE', 'DELETE'], pattern="platforms/*") # mapping from the real topic into the replacement. self.replaced_topic_map = {} # mapping from md5 hash of address to the actual connection to the # remote instance. self.vcp_connections = {} # Current sessions available to the self.web_sessions = None # Platform health based upon device driver publishes self.device_health = defaultdict(dict) # Used to hold scheduled reconnection event for vcp agents. self._vcp_reconnect_event = None # the registered socket endpoints so we can send out management # events to all the registered session. self._websocket_endpoints = set() self._platforms = Platforms(self) self._platform_scan_event = None self._connected_platforms = dict()
class Exporter: global platforms platforms = Platforms() def __init__(self, datpath=None): # set default encoding to utf8 for parsing and logging # utf-8 characters in console and files # reload(sys) sys.setdefaultencoding('utf8') configure_logging(install_root_handler=False) logging.basicConfig(filename='export.log', filemode='a', format='%(levelname)s: %(message)s', level=logging.INFO) self.datpath = datpath if datpath: # parse the xml dat file # self.gamestree = ET.parse(self.datpath).getroot() # identify platform # if self.gamestree.find('.//platform') is None: logging.error('No platform element found!') logging.error('Add a <platform/> xml element in the dat file.') logging.error( 'e.g. <header>..<platform>Genesis</platform/>..</header>') return else: self.platform = self.gamestree.find('.//platform').text logging.info('Platform:' + self.platform) platformId = platforms.getId(self.platform) if platformId is None: logging.error('Platform ' + self.platform + ' not supported.') return # strip whitespace and remove text within parenetheses # from game names. # load adictionary with game name as key and a list # of the regions it released # # e.g. the folloging xml entry # # <game name="NBA Live 95 (USA, Europe)"> # <description>NBA Live 95 (USA, Europe)</description> # <release name="NBA Live 95 (USA, Europe)" region="EUR"/> # <release name="NBA Live 95 (USA, Europe)" region="USA"/> # <rom name="NBA Live 95 (USA, Europe).md" size="2097152" crc="66018ABC" /> # </game> # # is loaded in the games{} dictionary as: # {'NBA Live 95': ['EUR', 'USA']} # self.games = defaultdict(list) self.romnames = defaultdict(list) urls = [] for g in self.gamestree.findall('game'): if not g.attrib.get('cloneof'): # For Mame ( Arcade ) platform, read the name of the game # from <description> tag. # That is because MAME dat files, in the name attribute store # the romname instead of the game title. # This is an exception for the Arcade platform only. # if self.platform == 'Arcade': name = self.normalizeName(g.find('./description').text) else: name = self.normalizeName(g.attrib.get('name')) name = name.encode('utf-8') if not name in self.games.keys(): romname = '' rom = g.find('./rom/[1]') if rom != None: romname = rom.attrib.get('name') self.romnames[name].append(romname) releases = g.findall('release') if releases: for r in releases: self.games[name].append(r.attrib.get('region')) else: self.games[name].append('Worldwide') for g in self.games: logging.debug('Submitting title:' + g + ':' + str(self.games[g][0])) urls.append('http://mobygames.com/search/quick' + '?q=' + g + '&p=' + platformId + '&search=Go' '&sFilter=1' '&sG=on' '&search_title=' + urllib.quote(g) + '&search_platform=' + urllib.quote(self.platform) + '&search_region=' + urllib.quote(self.games[g][0]) + '&romname=' + urllib.quote(self.romnames[g][0])) process = CrawlerProcess(get_project_settings()) process.crawl(MobygamesSpider, start_urls=urls) process.start() else: logging.warning('No dat file.') # strip all text in parentheses # def normalizeName(self, value): value = self.remove_parentheses(value) value = self.remove_brackets(value) return value.strip() # remove parentheses and text inside them def remove_parentheses(self, x): if '(' in x and ')' in x: x = re.sub('\(.*?\)', '', x) return x # remove brackets and text inside them def remove_brackets(self, x): if '[' in x and ']' in x: x = re.sub('\[.*?\]', '', x) return x
def init(self): self.mode = "mainmenu" self.module = Module(self.width//2,self.height//2+10) self.essentialplatforms = [Platforms(self.width//2,self.height-20),\ Platforms(random.randint(0,350),self.height-80),\ Platforms(random.randint(0,350),self.height-140),\ Platforms(random.randint(0,350),self.height-200),\ Platforms(random.randint(0,350),self.height-260),\ Platforms(random.randint(0,350),self.height-320),\ Platforms(random.randint(0,350),self.height-380)] #essential platforms for prevention of getting "stuck" self.platforms = [] self.platforms = JumpGame.getplatforms(10,self.essentialplatforms,\ self.platforms,0,350,20,370) #additional platforms scattered around. self.aliens = [] self.projectiles = [] self.enemyaircraft = [] self.enemyprojectiles = [] self.powerups = [] self.startmodule = MainMenuModule(self.width//2+self.width//4+7,\ self.height//2+50) self.startplatform = Platforms(self.width//2+self.width//4-10,\ self.height - 30) self.instructionsmodule = MainMenuModule(self.width//10,105) self.instructionsalien = Alien(self.width//18,160) self.instructionsaircraft = EnemyAircraft(self.width//15+30,100) self.ground = 350 self.maxplatforms = 10 self.maxaliens = 2 self.maxenemyaircrafts = 1 #max is for overload prevention self.maxenemyprojectiles = 7 self.maxpowerups = 2 self.deployable = True self.hitcount = 5 self.aircrafthit = False self.movejump = True self.storage = None self.background = \ PhotoImage(\ file="/Users/Edward/Desktop/TermProject/Images/background1.gif") #https://media.giphy.com/media/8xK1CusSFTI0U/giphy.gif self.score = 0 self.once = False #for updating aliens self.gameover = False self.fuel = False self.fuelscore = None self.protection = False self.protectionscore = None self.fuelprotection = False #for when on fuel self.donespeed = None self.extrajumps = 3 self.extrajumpscore = 0 self.canshoot = True self.canshoottime = 0 self.oldcanshoottime = 0 self.ringcolor = "gold" self.dx = 0 self.dy = -10 ###RACE self.computer = Computer(self.width//2,self.height//2+10) self.currentplatform = None self.targetplatform = None self.oldtargetplatform = None self.movement = None self.first = False self.stopmoving = False self.racescore = 0 self.youwin = False self.youlose = False self.finishline = True self.finish = []
def __init__(self): # Settings pygame.mixer.init() pygame.mixer.music.load('latenight.ogg') pygame.mixer.music.play(0) self.WIDTH = 640 self.HEIGHT = 360 # Config self.tps_max = 100 # Initialization pygame.init() font = pygame.font.SysFont("Arial", 18) self.resolution = (self.screen_width, self.screen_height) = (self.WIDTH, self.HEIGHT) self.screen = pygame.display.set_mode(self.resolution, pygame.RESIZABLE) self.tps_clock = pygame.time.Clock() self.tps_delta = 0.0 self.scroll = Vector2(0, 0) self.map = Map(self) self.player = Player( self ) # przy inicjalizacji przekazuje playerowi wszystko Player(self) self.enemy = Enemy(self) self.weapon = Weapon(self) self.fire = Fire(self) self.physics = Physics(self) self.platforms = Platforms(self) self.collision = Collision(self) self.sprite = Sprite(self) self.menu = Menu(self) self.file_loader = FileLoader(self) self.sprite.load_images() def create_fonts(font_sizes_list): "Creates different fonts with one list" fonts = [] for size in font_sizes_list: fonts.append(pygame.font.SysFont("Arial", size)) return fonts def render(fnt, what, color, where): "Renders the fonts as passed from display_fps" text_to_show = fnt.render(what, 0, pygame.Color(color)) self.screen.blit(text_to_show, where) def display_fps(): "Data that will be rendered and blitted in _display" render(fonts[0], what=str(int(self.tps_clock.get_fps())), color="white", where=(0, 0)) fonts = create_fonts([32, 16, 14, 8]) while True: # Events for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit(0) elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: ############# klik i cos sie dzieje raz sys.exit(0) # Ticking self.tps_delta += self.tps_clock.tick( ) / 1000.0 # zamieniam MS na sekundy while self.tps_delta > 1 / self.tps_max: self.tick() self.tps_delta -= 1 / self.tps_max # Rendering/Drawing self.screen.fill((0, 0, 0)) self.draw() display_fps() pygame.display.flip()
def __init__(self, config_path, **kwargs): """ Creates a `VolttronCentralAgent` object to manage instances. Each instances that is registered must contain a running `VolttronCentralPlatform`. Through this conduit the `VolttronCentralAgent` is able to communicate securly and efficiently. :param config_path: :param kwargs: :return: """ _log.info("{} constructing...".format(self.__class__.__name__)) super(VolttronCentralAgent, self).__init__(enable_web=True, **kwargs) # Load the configuration into a dictionary config = utils.load_config(config_path) # Required users users = config.get('users', None) # Expose the webroot property to be customized through the config # file. webroot = config.get('webroot', DEFAULT_WEB_ROOT) if webroot.endswith('/'): webroot = webroot[:-1] topic_replace_list = config.get('topic-replace-list', []) # Create default configuration to be used in case of problems in the # packaged agent configuration file. self.default_config = dict( webroot=os.path.abspath(webroot), users=users, topic_replace_list=topic_replace_list ) # During the configuration update/new/delete action this will be # updated to the current configuration. self.runtime_config = None # Start using config store. self.vip.config.set_default("config", config) self.vip.config.subscribe(self.configure_main, actions=['NEW', 'UPDATE', 'DELETE'], pattern="config") # Use config store to update the settings of a platform's configuration. self.vip.config.subscribe(self.configure_platforms, actions=['NEW', 'UPDATE', 'DELETE'], pattern="platforms/*") # mapping from the real topic into the replacement. self.replaced_topic_map = {} # mapping from md5 hash of address to the actual connection to the # remote instance. self.vcp_connections = {} # Current sessions available to the self.web_sessions = None # Platform health based upon device driver publishes self.device_health = defaultdict(dict) # Used to hold scheduled reconnection event for vcp agents. self._vcp_reconnect_event = None # the registered socket endpoints so we can send out management # events to all the registered session. self._websocket_endpoints = set() self._platforms = Platforms(self) self._platform_scan_event = None self._connected_platforms = dict()
class VolttronCentralAgent(Agent): """ Agent for managing many volttron instances from a central web ui. During the """ def __init__(self, config_path, **kwargs): """ Creates a `VolttronCentralAgent` object to manage instances. Each instances that is registered must contain a running `VolttronCentralPlatform`. Through this conduit the `VolttronCentralAgent` is able to communicate securly and efficiently. :param config_path: :param kwargs: :return: """ _log.info("{} constructing...".format(self.__class__.__name__)) super(VolttronCentralAgent, self).__init__(enable_web=True, **kwargs) # Load the configuration into a dictionary config = utils.load_config(config_path) # Required users users = config.get('users', None) # Expose the webroot property to be customized through the config # file. webroot = config.get('webroot', DEFAULT_WEB_ROOT) if webroot.endswith('/'): webroot = webroot[:-1] topic_replace_list = config.get('topic-replace-list', []) # Create default configuration to be used in case of problems in the # packaged agent configuration file. self.default_config = dict( webroot=os.path.abspath(webroot), users=users, topic_replace_list=topic_replace_list ) # During the configuration update/new/delete action this will be # updated to the current configuration. self.runtime_config = None # Start using config store. self.vip.config.set_default("config", config) self.vip.config.subscribe(self.configure_main, actions=['NEW', 'UPDATE', 'DELETE'], pattern="config") # Use config store to update the settings of a platform's configuration. self.vip.config.subscribe(self.configure_platforms, actions=['NEW', 'UPDATE', 'DELETE'], pattern="platforms/*") # mapping from the real topic into the replacement. self.replaced_topic_map = {} # mapping from md5 hash of address to the actual connection to the # remote instance. self.vcp_connections = {} # Current sessions available to the self.web_sessions = None # Platform health based upon device driver publishes self.device_health = defaultdict(dict) # Used to hold scheduled reconnection event for vcp agents. self._vcp_reconnect_event = None # the registered socket endpoints so we can send out management # events to all the registered session. self._websocket_endpoints = set() self._platforms = Platforms(self) self._platform_scan_event = None self._connected_platforms = dict() @staticmethod def _get_next_time_seconds(seconds=10): now = get_aware_utc_now() next_time = now + datetime.timedelta(seconds=seconds) return next_time def _handle_platform_connection(self, platform_vip_identity): _log.info("Handling new platform connection {}".format( platform_vip_identity)) platform = self._platforms.add_platform(platform_vip_identity) def _handle_platform_disconnect(self, platform_vip_identity): _log.warn("Handling disconnection of connection from identity: {}".format( platform_vip_identity )) # TODO send alert that there was a platform disconnect. self._platforms.disconnect_platform(platform_vip_identity) def _scan_for_platforms(self): """ Scan the local bus for peers that start with 'vcp-'. Handle the connection and disconnection events here. """ if self._platform_scan_event is not None: # This won't hurt anything if we are canceling ourselves. self._platform_scan_event.cancel() # Identities of all platform agents that are connecting to us should # have an identity of platform.md5hash. connected_platforms = set([x for x in self.vip.peerlist().get(timeout=5) if x.startswith('vcp-')]) disconnected = self._platforms.get_platform_keys() - connected_platforms for vip_id in disconnected: self._handle_platform_disconnect(vip_id) not_known = connected_platforms - self._platforms.get_platform_keys() for vip_id in not_known: self._handle_platform_connection(vip_id) next_platform_scan = VolttronCentralAgent._get_next_time_seconds() # reschedule the next scan. self._platform_scan_event = self.core.schedule( next_platform_scan, self._scan_for_platforms) def configure_main(self, config_name, action, contents): """ The main configuration for volttron central. This is where validation will occur. Note this method is called: 1. When the agent first starts (with the params from packaged agent file) 2. When 'store' is called through the volttron-ctl config command line with 'config' as the name. Required Configuration: The volttron central requires a user mapping. :param config_name: :param action: :param contents: """ _log.debug('Main config updated') _log.debug('ACTION IS {}'.format(action)) _log.debug('CONTENT IS {}'.format(contents)) if action == 'DELETE': # Remove the registry and keep the service running. self.runtime_config = None # Now stop the exposition of service. else: self.runtime_config = self.default_config.copy() self.runtime_config.update(contents) problems = self._validate_config_params(self.runtime_config) if len(problems) > 0: _log.error( "The following configuration problems were detected!") for p in problems: _log.error(p) sys.exit(INVALID_CONFIGURATION_CODE) else: _log.info('volttron central webroot is: {}'.format( self.runtime_config.get('webroot') )) users = self.runtime_config.get('users') self.web_sessions = SessionHandler(Authenticate(users)) _log.debug('Querying router for addresses and serverkey.') q = Query(self.core) external_addresses = q.query('addresses').get(timeout=5) self.runtime_config['local_external_address'] = external_addresses[0] self.vip.web.register_websocket(r'/vc/ws', self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) self.vip.web.register_endpoint(r'/jsonrpc', self.jsonrpc) self.vip.web.register_path(r'^/.*', self.runtime_config.get('webroot')) # Start scanning for new platforms connections as well as for # disconnects that happen. self._scan_for_platforms() def configure_platforms(self, config_name, action, contents): _log.debug('Platform configuration updated.') _log.debug('ACTION IS {}'.format(action)) _log.debug('CONTENT IS {}'.format(contents)) def open_authenticate_ws_endpoint(self, fromip, endpoint): """ Callback method from when websockets are opened. The endpoine must be '/' delimited with the second to last section being the session of a logged in user to volttron central itself. :param fromip: :param endpoint: A string representing the endpoint of the websocket. :return: """ _log.debug("OPENED ip: {} endpoint: {}".format(fromip, endpoint)) try: session = endpoint.split('/')[-2] except IndexError: _log.error("Malformed endpoint. Must be delimited by '/'") _log.error( 'Endpoint must have valid session in second to last position') return False if not self.web_sessions.check_session(session, fromip): _log.error("Authentication error for session!") return False _log.debug('Websocket allowed.') self._websocket_endpoints.add(endpoint) return True def _ws_closed(self, endpoint): _log.debug("CLOSED endpoint: {}".format(endpoint)) try: self._websocket_endpoints.remove(endpoint) except KeyError: pass # This should never happen but protect against it anyways. def _ws_received(self, endpoint, message): _log.debug("RECEIVED endpoint: {} message: {}".format(endpoint, message)) @RPC.export def is_registered(self, address_hash=None, address=None): if address_hash is None and address is None: return False if address_hash is None: address_hash = PlatformHandler.address_hasher(address) return self._platforms.is_registered(address_hash) @RPC.export def get_publickey(self): """ RPC method allowing the caller to retrieve the publickey of this agent. This method is available for allowing :class:`VolttronCentralPlatform` agents to allow this agent to be able to connect to its instance. :return: The publickey of this volttron central agent. :rtype: str """ return self.core.publickey @RPC.export def unregister_platform(self, platform_uuid): _log.debug('unregister_platform') platform = self._registered_platforms.get(platform_uuid) if platform: connected = self._platform_connections.get(platform_uuid) if connected is not None: connected.call('unmanage') connected.kill() address = None for v in self._address_to_uuid.values(): if v == platform_uuid: address = v break if address: del self._address_to_uuid[address] del self._platform_connections[platform_uuid] del self._registered_platforms[platform_uuid] self._registered_platforms.sync() context = 'Unregistered platform {}'.format(platform_uuid) return {'status': 'SUCCESS', 'context': context} else: msg = 'Unable to unregistered platform {}'.format(platform_uuid) return {'error': {'code': UNABLE_TO_UNREGISTER_INSTANCE, 'message': msg}} def _to_jsonrpc_obj(self, jsonrpcstr): """ Convert data string into a JsonRpcData named tuple. :param object data: Either a string or a dictionary representing a json document. """ return jsonrpc.JsonRpcData.parse(jsonrpcstr) def jsonrpc(self, env, data): """ The main entry point for ^jsonrpc data This method will only accept rpcdata. The first time this method is called, per session, it must be using get_authorization. That will return a session token that must be included in every subsequent request. The session is tied to the ip address of the caller. :param object env: Environment dictionary for the request. :param object data: The JSON-RPC 2.0 method to call. :return object: An JSON-RPC 2.0 response. """ if env['REQUEST_METHOD'].upper() != 'POST': return jsonrpc.json_error('NA', INVALID_REQUEST, 'Invalid request method, only POST allowed' ) try: rpcdata = self._to_jsonrpc_obj(data) _log.info('rpc method: {}'.format(rpcdata.method)) if rpcdata.method == 'get_authorization': args = {'username': rpcdata.params['username'], 'password': rpcdata.params['password'], 'ip': env['REMOTE_ADDR']} sess = self.web_sessions.authenticate(**args) if not sess: _log.info('Invalid username/password for {}'.format( rpcdata.params['username'])) return jsonrpc.json_error( rpcdata.id, UNAUTHORIZED, "Invalid username/password specified.") _log.info('Session created for {}'.format( rpcdata.params['username'])) self.vip.web.register_websocket( "/vc/ws/{}/management".format(sess), self.open_authenticate_ws_endpoint, self._ws_closed, self._received_data) _log.info('Session created for {}'.format( rpcdata.params['username'])) gevent.sleep(1) return jsonrpc.json_result(rpcdata.id, sess) token = rpcdata.authorization ip = env['REMOTE_ADDR'] _log.debug('REMOTE_ADDR: {}'.format(ip)) session_user = self.web_sessions.check_session(token, ip) _log.debug('SESSION_USER IS: {}'.format(session_user)) if not session_user: _log.debug("Session Check Failed for Token: {}".format(token)) return jsonrpc.json_error(rpcdata.id, UNAUTHORIZED, "Invalid authentication token") _log.debug('RPC METHOD IS: {}'.format(rpcdata.method)) # Route any other method that isn't result_or_error = self._route_request(session_user, rpcdata.id, rpcdata.method, rpcdata.params) except AssertionError: return jsonrpc.json_error( 'NA', INVALID_REQUEST, 'Invalid rpc data {}'.format(data)) except Unreachable: return jsonrpc.json_error( rpcdata.id, UNAVAILABLE_PLATFORM, "Couldn't reach platform with method {} params: {}".format( rpcdata.method, rpcdata.params)) except Exception as e: return jsonrpc.json_error( 'NA', UNHANDLED_EXCEPTION, e ) return self._get_jsonrpc_response(rpcdata.id, result_or_error) def _get_jsonrpc_response(self, id, result_or_error): """ Wrap the response in either a json-rpc error or result. :param id: :param result_or_error: :return: """ if isinstance(result_or_error, dict): if 'jsonrpc' in result_or_error: return result_or_error if result_or_error is not None and isinstance(result_or_error, dict): if 'error' in result_or_error: error = result_or_error['error'] _log.debug("RPC RESPONSE ERROR: {}".format(error)) return jsonrpc.json_error(id, error['code'], error['message']) return jsonrpc.json_result(id, result_or_error) def _get_agents(self, instance_uuid, groups): """ Retrieve the list of agents on a specific platform. :param instance_uuid: :param groups: :return: """ _log.debug('_get_agents') connected_to_pa = self._platform_connections[instance_uuid] agents = connected_to_pa.agent.vip.rpc.call( 'platform.agent', 'list_agents').get(timeout=30) for a in agents: if 'admin' in groups: if "platformagent" in a['name'] or \ "volttroncentral" in a['name']: a['vc_can_start'] = False a['vc_can_stop'] = False a['vc_can_restart'] = True else: a['vc_can_start'] = True a['vc_can_stop'] = True a['vc_can_restart'] = True else: # Handle the permissions that are not admin. a['vc_can_start'] = False a['vc_can_stop'] = False a['vc_can_restart'] = False _log.debug('Agents returned: {}'.format(agents)) return agents def _setupexternal(self): _log.debug(self.vip.ping('', "PING ROUTER?").get(timeout=3)) def _configure_agent(self, endpoint, message): _log.debug('Configure agent: {} message: {}'.format(endpoint, message)) def _received_data(self, endpoint, message): print('Received from endpoint {} message: {}'.format(endpoint, message)) self.vip.web.send(endpoint, message) def set_setting(self, session_user, params): """ Sets or removes a setting from the config store. If the value is None then the item will be removed from the store. If there is an error in saving the value then a jsonrpc.json_error object is returned. :param session_user: Unused :param params: Dictionary that must contain 'key' and 'value' keys. :return: A 'SUCCESS' string or a jsonrpc.json_error object. """ if 'key' not in params or not params['key']: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') if 'value' not in params: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') config_key = "settings/{}".format(params['key']) value = params['value'] if value is None: try: self.vip.config.delete(config_key) except KeyError: pass else: # We handle empt string here because the config store doesn't allow # empty strings to be set as a config store. I wasn't able to # trap the ValueError that is raised on the server side. if value == "": return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid value set (empty string?)') self.vip.config.set(config_key, value) return 'SUCCESS' def get_setting(self, session_user, params): """ Retrieve a value from the passed setting key. The params object must contain a "key" to return from the settings store. :param session_user: Unused :param params: Dictionary that must contain a 'key' key. :return: The value or a jsonrpc error object. """ config_key = "settings/{}".format(params['key']) try: value = self.vip.config.get(config_key) except KeyError: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid key specified') else: return value def get_setting_keys(self, session_user, params): """ Returns a list of all of the settings keys so the caller can know what settings to request. :param session_user: Unused :param params: Unused :return: A list of settings available to the caller. """ prefix = "settings/" keys = [x[len(prefix):] for x in self.vip.config.list() if x.startswith(prefix)] return keys or [] def _handle_bacnet_props(self, session_user, params): platform_uuid = params.pop('platform_uuid') id = params.pop('message_id') _log.debug('Handling bacnet_props platform: {}'.format(platform_uuid)) configure_topic = "{}/configure".format(session_user['token']) ws_socket_topic = "/vc/ws/{}".format(configure_topic) if configure_topic not in self._websocket_endpoints: self.vip.web.register_websocket(ws_socket_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) def start_sending_props(): response_topic = "configure/{}".format(session_user['token']) # Two ways we could have handled this is to pop the identity off # of the params and then passed both the identity and the response # topic. Or what I chose to do and to put the argument in a # copy of the params. cp = params.copy() cp['publish_topic'] = response_topic cp['device_id'] = int(cp['device_id']) platform = self._platforms.get_platform(platform_uuid) _log.debug('PARAMS: {}'.format(cp)) platform.call("publish_bacnet_props", **cp) gevent.spawn_later(2, start_sending_props) def _handle_bacnet_scan(self, session_user, params): platform_uuid = params.pop('platform_uuid') id = params.pop('message_id') _log.debug('Handling bacnet_scan platform: {}'.format(platform_uuid)) if not self._platforms.is_registered(platform_uuid): return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) scan_length = params.pop('scan_length', 5) try: scan_length = float(scan_length) params['scan_length'] = scan_length platform = self._platforms.get_platform(platform_uuid) iam_topic = "{}/iam".format(session_user['token']) ws_socket_topic = "/vc/ws/{}".format(iam_topic) self.vip.web.register_websocket(ws_socket_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) def start_scan(): # We want the datatype (iam) to be second in the response so # we need to reposition the iam and the session id to the topic # that is passed to the rpc function on vcp iam_session_topic = "iam/{}".format(session_user['token']) platform.call("start_bacnet_scan", iam_session_topic, **params) def close_socket(): _log.debug('Closing bacnet scan for {}'.format( platform_uuid)) gevent.spawn_later(2, self.vip.web.unregister_websocket, iam_session_topic) gevent.spawn_later(scan_length, close_socket) # By starting the scan a second later we allow the websocket # client to subscribe to the newly available endpoint. gevent.spawn_later(2, start_scan) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) except KeyError: return jsonrpc.json_error(id, UNAUTHORIZED, "Invalid user session token") def _enable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error enable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to enable setup mode") auth_file = AuthFile() entries = auth_file.find_by_credentials(".*") if len(entries) > 0: return "SUCCESS" entry = AuthEntry(credentials="/.*/", comments="Un-Authenticated connections allowed here", user_id="unknown") auth_file.add(entry) return "SUCCESS" def _disable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error disable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to disable setup mode") auth_file = AuthFile() auth_file.remove_by_credentials("/.*/") return "SUCCESS" def _handle_management_endpoint(self, session_user, params): ws_topic = "/vc/ws/{}/management".format(session_user.get('token')) self.vip.web.register_websocket(ws_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) return ws_topic def send_management_message(self, type, data={}): """ Send a message to any socket that has connected to the management socket. The payload sent to the client is like the following:: { "type": "UPDATE_DEVICE_STATUS", "data": "this is data that was passed" } :param type: A string defining a unique type for sending to the websockets. :param data: An object that str can be called on. :type type: str :type data: serializable """ management_sockets = [s for s in self._websocket_endpoints if s.endswith("management")] # Nothing to send if we don't have any management sockets open. if len(management_sockets) <= 0: return if data is None: data = {} payload = dict( type=type, data=str(data) ) payload = jsonapi.dumps(payload) for s in management_sockets: self.vip.web.send(s, payload) def _route_request(self, session_user, id, method, params): """ Handle the methods volttron central can or pass off to platforms. :param session_user: The authenticated user's session info. :param id: JSON-RPC id field. :param method: :param params: :return: """ _log.debug( 'inside _route_request {}, {}, {}'.format(id, method, params)) def err(message, code=METHOD_NOT_FOUND): return {'error': {'code': code, 'message': message}} self.send_management_message(method) method_split = method.split('.') # The last part of the jsonrpc method is the actual method to be called. method_check = method_split[-1] # These functions will be sent to a platform.agent on either this # instance or another. All of these functions have the same interface # and can be collected into a dictionary rather than an if tree. platform_methods = dict( # bacnet related start_bacnet_scan=self._handle_bacnet_scan, publish_bacnet_props=self._handle_bacnet_props, # config store related store_agent_config="store_agent_config", get_agent_config="get_agent_config", list_agent_configs="get_agent_config_list", # management related list_agents="get_agent_list", get_devices="get_devices", status_agents="status_agents" ) # These methods are specifically to be handled by the platform not any # agents on the platform that is why we have the length requirement. # # The jsonrpc method looks like the following # # platform.uuid.<dynamic entry>.method_on_vcp if method_check in platform_methods: platform_uuid = None if isinstance(params, dict): platform_uuid = params.pop('platform_uuid', None) if platform_uuid is None: if method_split[0] == 'platforms' and method_split[1] == 'uuid': platform_uuid = method_split[2] if not platform_uuid: return err("Invalid platform_uuid specified as parameter" .format(platform_uuid), INVALID_PARAMS) if not self._platforms.is_registered(platform_uuid): return err("Unknown or unavailable platform {} specified as " "parameter".format(platform_uuid), UNAVAILABLE_PLATFORM) try: _log.debug('Calling {} on platform {}'.format( method_check, platform_uuid )) class_method = platform_methods[method_check] platform = self._platforms.get_platform(platform_uuid) # Determine whether the method to call is on the current class # or on the platform object. if isinstance(class_method, basestring): method_ref = getattr(platform, class_method) else: method_ref = class_method # Put the platform_uuid in the params so it can be used # inside the method params['platform_uuid'] = platform_uuid except AttributeError or KeyError: return jsonrpc.json_error(id, INTERNAL_ERROR, "Attempted calling function " "{} was unavailable".format( class_method )) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform " "{}".format(platform_uuid)) else: # pass the id through the message_id parameter. if not params: params = dict(message_id=id) else: params['message_id'] = id # Methods will all have the signature # method(session, params) # return method_ref(session_user, params) vc_methods = dict( register_management_endpoint=self._handle_management_endpoint, list_platforms=self._platforms.get_platform_list, list_performance=self._platforms.get_performance_list, # Settings set_setting=self.set_setting, get_setting=self.get_setting, get_setting_keys=self.get_setting_keys, # Setup mode enable_setup_mode=self._enable_setup_mode, disable_setup_mode=self._disable_setup_mode ) if method in vc_methods: if not params: params = dict(message_id=id) else: params['message_id'] = id response = vc_methods[method](session_user, params) _log.debug("Response is {}".format(response)) return response # vc_methods[method](session_user, params) if method == 'register_instance': if isinstance(params, list): return self._register_instance(*params) else: return self._register_instance(**params) elif method == 'unregister_platform': return self.unregister_platform(params['instance_uuid']) elif 'historian' in method: has_platform_historian = PLATFORM_HISTORIAN in \ self.vip.peerlist().get(timeout=30) if not has_platform_historian: return err( 'The VOLTTRON Central platform historian is unavailable.', UNAVAILABLE_AGENT) _log.debug('Trapping platform.historian to vc.') _log.debug('has_platform_historian: {}'.format( has_platform_historian)) if 'historian.query' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'query', **params).get(timeout=30) elif 'historian.get_topic_list' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'get_topic_list').get(timeout=30) # This isn't known as a proper method on vc or a platform. if len(method_split) < 3: return err('Unknown method {}'.format(method)) if method_split[0] != 'platforms' or method_split[1] != 'uuid': return err('Invalid format for instance must start with ' 'platforms.uuid') instance_uuid = method_split[2] _log.debug('Instance uuid is: {}'.format(instance_uuid)) if not self._platforms.is_registered(instance_uuid): return err('Unknown platform {}'.format(instance_uuid)) platform_method = '.'.join(method_split[3:]) _log.debug("Platform method is: {}".format(platform_method)) platform = self._platforms.get_platform(instance_uuid) if not platform: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "cannot connect to platform." ) if platform_method.startswith('install'): if 'admin' not in session_user['groups']: return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to install agents") return platform.route_to_agent_method(id, platform_method, params) def _validate_config_params(self, config): """ Validate the configuration parameters of the default/updated parameters. This method will return a list of "problems" with the configuration. If there are no problems then an empty list is returned. :param config: Configuration parameters for the volttron central agent. :type config: dict :return: The problems if any, [] if no problems :rtype: list """ problems = [] webroot = config.get('webroot') if not webroot: problems.append('Invalid webroot in configuration.') elif not os.path.exists(webroot): problems.append( 'Webroot {} does not exist on machine'.format(webroot)) users = config.get('users') if not users: problems.append('A users node must be specified!') else: has_admin = False try: for user, item in users.items(): if 'password' not in item.keys(): problems.append('user {} must have a password!'.format( user)) elif not item['password']: problems.append('password for {} is blank!'.format( user )) if 'groups' not in item.keys(): problems.append('missing groups key for user {}'.format( user )) elif not isinstance(item['groups'], list): problems.append('groups must be a list of strings.') elif not item['groups']: problems.append( 'user {} must belong to at least one group.'.format( user)) # See if there is an adminstator present. if not has_admin and isinstance(item['groups'], list): has_admin = 'admin' in item['groups'] except AttributeError: problems.append('invalid user node.') if not has_admin: problems.append("One user must be in the admin group.") return problems
class SleepyMeshBase(DatabaseDict): def __init__(self, system_settings, websocket, snmp_websocket, **kwargs): super(SleepyMeshBase, self).__init__(**kwargs) if 'last_syncs' not in self._defaults: self._defaults.update({'last_syncs': list()}) # Internal Members # self._mesh_awake = True self._sync_type = 'timeout' self._save_in_progress = False self._sync_average = None self._delay_average = None # Instances # # TODO: Eliminate as many dependencies as possible self.system_settings = system_settings self.websocket = websocket self.snmp_websocket = snmp_websocket self.modbus_server = ModbusServer() self.snmp_server = SNMPTrapServer(self) self.update_interfaces = UpdateInterfaces(self) self.update_in_progress = self.update_interfaces.update_in_progress self.bridge = Bridge(self.system_settings) self.uploader = Uploader(self) self.nodes = Nodes(self.system_settings) self.platforms = Platforms(self.nodes) self.networks = Networks(self) self.error = BaseError(self.system_settings) if self.system_settings.modbus_enable: system_settings_dict = self.system_settings.attr_dict() # LOGGER.debug('Modbus Attribute Dictionary: ' + str(system_settings_dict)) self.modbus_server.start(system_settings_dict) if self.system_settings.snmp_enable: self.snmp_server.start() # Overload Node Error Methods (SNMP Error Methods)# NodeError.send_snmp = self.snmp_server.send_snmp NodeError.clear_snmp = self.snmp_server.clear_snmp ## Public/Private Methods ## # Sleep/Wake Period Methods # def sleep_period(self): """ Fetches sleep period """ return self.networks[0]['sleep'] def _wake_period(self): """ Fetches wake period """ return self.networks[0]['wake'] def wake_period(self): """ Fetches wake period (or dynamic wake period if enabled) """ static_wake_period = self._wake_period() output = static_wake_period if DYNAMIC_WAKE_PERIOD: if self._sync_average is not None: dynamic_wake_period = self._sync_average + SYNC_DEVIATION if dynamic_wake_period < static_wake_period: output = dynamic_wake_period return output def save(self, db_content=None): """ Overloading default save method """ self._save_in_progress = True # Save statistics data super(SleepyMeshBase, self).save(db_content) self.platforms.save() if self.system_settings.modbus_enable: self.modbus_server.save() if self.system_settings.snmp_enable: self.snmp_server.save() self._save_in_progress = False ## Private Methods ## # Last Sync Related Methods # def _update_last_sync(self): """ Updates last sync """ last_sync = self.system_settings.time() self['last_syncs'].append(last_sync) if len(self['last_syncs']) > LAST_SYNCS_NUMBER: del self['last_syncs'][0] def _ct_ls(self): """ Returns current system time - last sync value """ output = 0 if len(self['last_syncs']): output = self.system_settings.time() - self['last_syncs'][-1] return output def _ct_ls_str(self, ct_ls=None): """ Returns current system time - last sync string """ if ct_ls is None: ct_ls = self._ct_ls() output = CT_LS + "{0:.3f}".format(ct_ls) return output
class ParentClone: global platforms platforms = Platforms() def __init__(self, datpath=None): self.datpath = datpath if datpath: # parse the xml dat file # self.gamestree = ET.parse(self.datpath).getroot() # identify platform # if self.gamestree.find('.//platform') is None: logging.error('No platform element found!') logging.error('Add a <platform/> xml element in the dat file.') logging.error( 'e.g. <header>..<platform>Genesis</platform/>..</header>') return else: self.platform = self.gamestree.find('.//platform').text logging.info('Platform:' + self.platform) if platforms.getId(self.platform) is None: logging.error('Platform ' + self.platform + ' not supported.') return # strip whitespace and remove text within parenetheses # from game names. # load adictionary with game name as key and a list # of the regions it released # # e.g. the folloging xml entry # # <game name="NBA Live 95 (USA, Europe)"> # <description>NBA Live 95 (USA, Europe)</description> # <release name="NBA Live 95 (USA, Europe)" region="EUR"/> # <release name="NBA Live 95 (USA, Europe)" region="USA"/> # <rom name="NBA Live 95 (USA, Europe).md" size="2097152" crc="66018ABC" /> # </game> # # is loaded in the games{} dictionary as: # {'NBA Live 95': ['EUR', 'USA']} # self.games = defaultdict(list) for g in self.gamestree.findall('game'): if not g.attrib.get('cloneof'): name = self.normalizeName(g.attrib.get('name')) for r in g.findall('release'): self.games[name].append(r.attrib.get('region')) process = CrawlerProcess(get_project_settings()) process.crawl( 'mobygames', url= 'http://mobygames.com/search/quick?q=air diver&p=16&search=Go&sFilter=1&sG=on' ) process.crawl( 'mobygames', url= 'http://mobygames.com/search/quick?q=sonic r&p=16&search=Go&sFilter=1&sG=on' ) # process.crawl(MobygamesSpider(url='http://mobygames.com/search/quick?q=air diver&p=16&search=Go&sFilter=1&sG=on')) process.start() # init env. configuration and runner # #configure_logging() #get_project_settings() #runner = CrawlerRunner() #runner.crawl(MobygamesSpider(title='air diver',platform='genesis', region='EUR')) #d = runner.join() #d.addBoth(lambda _: reactor.stop()) # the script will block here until all # crawling jobs are finished # #reactor.run() else: logging.warning('No dat file.') # strip all text in parentheses # def normalizeName(self, value): value = re.sub(r'\([^)]*\)', '', value) return value.strip()
class Importer: global platforms platforms = Platforms() def __init__(self, datpath=None, boxpath=None): logging.basicConfig( filename='import.log', filemode = 'a', format='%(levelname)s: %(message)s', level=logging.DEBUG ) settings = get_project_settings() imgpath = settings['IMAGES_STORE'] # paths of source and target file # source file is the exported one from the exporter.py # target file is LaunchBox.xml metadata file self.datpath = datpath self.boxpath = boxpath # counters for found and not found games self.inc_found = 0 self.inc_notfound = 0 # store titles of not found games self.notfound = [] if datpath and boxpath: self.dattree = ET.parse(self.datpath) self.dat = self.dattree.getroot() if self.dat.find('.//platform[1]') is None: logging.error('No platform element found!') logging.error('Add a <platform/> xml element in the dat file.') logging.error('e.g. <platform>Genesis</platform/>') return else: self.platform = self.dat.find('.//platform/[1]').text self.platform = platforms.getLaunchBoxPlatform(self.platform) self.imagesdir = self.boxpath + 'Images/' + self.platform + '/' self.frontdir = self.imagesdir + 'Box - Front/' self.backdir = self.imagesdir + 'Box - Back/' self.screenshotdir = self.imagesdir + 'Screenshot - Gameplay/Snap/' self.mediadir = self.imagesdir + 'Media/' if not os.path.exists(self.frontdir): os.makedirs(self.frontdir) if not os.path.exists(self.backdir): os.makedirs(self.backdir) if not os.path.exists(self.frontdir): os.makedirs(self.screenshotdir) if not os.path.exists(self.screenshotdir): os.makedirs(self.screenshotdir) if not os.path.exists(self.mediadir): os.makedirs(self.mediadir) logging.info('Platform:' + self.platform) logging.info('Images destination directory:' + self.imagesdir) # backup LaunchBox Data file # if self.boxpath: boxfile = self.boxpath + 'Data/Platforms/' + self.platform + '.xml' shutil.copyfile(boxfile, boxfile + '.bak') logging.info('Data directory:' + self.boxpath) logging.info('Data file:' + boxfile) logging.info('Data backup file:' + boxfile + '.bak') # Parse LaunchBox games for Platform # self.boxtree = ET.parse(boxfile) self.box = self.boxtree.getroot() # load game elements into dicts # with key == title, value game elemtn _datgames = self.dat.findall('game') _boxgames = self.box.findall('Game') self.datgames = defaultdict(list) self.boxgames = defaultdict(list) # load all games in the dat file in dictionary # by title # for g in _datgames: title = g.find('./title').text title = self.remove_prefix(title,"The ") title = self.remove_suffix(title,", The") title = self.remove_suffix(title,"The") title = self.normalizeName(title) title = filter(str.isalnum, title.encode('utf-8')).lower().strip() self.datgames[title] = g totalgames = len(self.datgames) # load all launchbox games for the given platform # and that exist in the dat file # for g in _boxgames: if g.find('Platform').text == self.platform: title = g.find('./Title').text title = self.remove_prefix(title,"The ") title = self.remove_suffix(title,", The") title = self.remove_suffix(title,"The") title = self.normalizeName(title) title = filter(str.isalnum, title.encode('utf-8')).lower().strip() self.boxgames[title] = g # delete keys from dat games that do not # have a match in launchbox # delkeys = [] for key in self.datgames: if key not in self.boxgames.keys(): delkeys.append(key) self.notfound.append(self.datgames[key].find('./title').text) self.inc_notfound += 1 else: self.inc_found += 1 for key in delkeys: del self.datgames[key] logging.info('Importing ' + str(self.inc_found) +'/' + str(totalgames) + ' games.') logging.info('Not found games:' + str(self.inc_notfound)) logging.info('List of not found games:' + str(self.notfound)) chars_to_remove = ['\\', '/', ':', '?', '\'', '"'] rx = '[' + re.escape(''.join(chars_to_remove)) + ']' # Start the import # for key in self.datgames: logging.debug('Importing ' + self.datgames[key].find('./title').text ) self.copyTag(key,'developer','Developer') self.copyTag(key,'publisher','Publisher') self.copyTag(key,'date','ReleaseDate') self.copyTag(key,'description','Notes') self.copyTag(key,'region','Region') self.copyTag(key,'source','Source') self.copyTag(key,'genre','Genre') self.copyTag(key,'group','Series') self.copyTag(key,'rating','Rating') gameId = self.boxgames[key].find('./ID').text # presentation custom field presentation = self.datgames[key].findall('./presentation//value') if len(presentation) > 0: for p in presentation: self.addCustomField(gameId,'Presentation',p.text) # perspective custom field perspective = self.datgames[key].findall('./perspective//value') if len(perspective) > 0: for p in perspective: self.addCustomField(gameId,'Perspective',p.text) # setting custom field setting = self.datgames[key].findall('./setting//value') if len(setting) > 0: for s in setting: self.addCustomField(gameId,'Setting',s.text) # pacing custom field pacing = self.datgames[key].findall('./pacing//value') if len(pacing) > 0: for p in pacing: self.addCustomField(gameId,'Pacing',p.text) # visual custom field visual = self.datgames[key].findall('./visual//value') if len(visual) > 0: for v in visual: self.addCustomField(gameId,'Visual',v.text) # vehicular custom field vehicular = self.datgames[key].findall('./vehicular//value') if len(vehicular) > 0: for v in vehicular: self.addCustomField(gameId,'Vehicular',v.text) # gameplay custom field gameplay = self.datgames[key].findall('./gameplay//value') if len(gameplay) > 0: for g in gameplay: self.addCustomField(gameId,'Gameplay',g.text) # narrative custom field narrative = self.datgames[key].findall('./narrative//value') if len(narrative) > 0: for n in narrative: self.addCustomField(gameId,'Narrative',n.text) # educational custom field educational = self.datgames[key].findall('./educational//value') if len(educational) > 0: for e in educational: self.addCustomField(gameId,'Educational',e.text) # sport custom field sport = self.datgames[key].findall('./sport//value') if len(sport) > 0: for s in sport: self.addCustomField(gameId,'Sport',s.text) # exclusive custom field exclusive = self.datgames[key].findall('./exclusive') if len(exclusive) > 0: self.addCustomField(gameId,'Exclusive','Yes') # mature custom field mature = self.datgames[key].findall('./mature') if len(mature) > 0: self.addCustomField(gameId,'Mature','Yes') # licensed custom field licensed = self.datgames[key].findall('./licensed') if len(licensed) > 0: self.addCustomField(gameId,'Licensed Title','Yes') # addon custom field addon = self.datgames[key].findall('./addon//value') if len(addon) > 0: for a in addon: self.addCustomField(gameId,'Add-on',a.text) # user score -> star rating userRating = self.datgames[key].findall('./userScore/value') if len(userRating) > 0: userRating =int(math.ceil(float(userRating[0].text))) self.boxgames[key].find('./StarRating').text = str(userRating) # IMAGES images = self.datgames[key].findall('./images//value') screenshots = [] media = [] title = self.boxgames[key].find('./Title').text title = re.sub(rx, '_', title) for img in images: url = img.find('./url').text path = imgpath +'/' + img.find('./path').text filename, file_extension = os.path.splitext(path) if 'front-cover' in url: dest = self.frontdir + title + file_extension shutil.copy(path,dest) elif 'back-cover' in url: dest = self.backdir + title + file_extension shutil.copy(path,dest) elif 'screenshot' in url: screenshots.append(path) else: media.append(path) self.copyImages(title,media, self.mediadir) self.copyImages(title,screenshots, self.screenshotdir) self.boxtree.write(boxfile + '.import',encoding='utf-8') else: logging.warning('No dat and/or box file.') # copies the text value inside a tag # def copyTag(self,key,source,target): game = self.datgames[key] _s = self.datgames[key].find('./' + source) if _s is not None: parent = self.boxgames[key] elem = parent.find('./' + target) if elem is not None: elem.text = _s.text else: elem = ET.SubElement(parent,target) elem.text = _s.text def addCustomField(self,gid,name,value): customfield = ET.SubElement(self.box,'CustomField') gidElem = ET.SubElement(customfield,'GameID') gidElem.text = gid nameElem = ET.SubElement(customfield,'Name') nameElem.text = name valueElem = ET.SubElement(customfield,'Value') valueElem.text = value def copyImages(self,title,images,targetdir): if len(images) > 0: for idx, val in enumerate(images): filename, file_extension = os.path.splitext(val) if idx < 9: dest = targetdir + title + '-0' + str(idx+1) + file_extension else: dest = targetdir + title + '-' + str(idx+1) + file_extension shutil.copy(val,dest) # strip all text in parentheses # def normalizeName(self,value): value = self.remove_parentheses(value) value = self.remove_brackets(value) return value.strip() # remove parentheses and text inside them def remove_parentheses(self,x): if '(' in x and ')' in x: x = re.sub('\(.*?\)','',x) return x # remove brackets and text inside them def remove_brackets(self,x): if '[' in x and ']' in x: x = re.sub('\[.*?\]','',x) return x # remove prefix from text x def remove_prefix(self,x, prefix): if x.startswith(prefix): x = x[len(prefix):] return x # remove suffix from text x def remove_suffix(self,x, suffix): if x.endswith(suffix): x = x[:-len(suffix):] return x
class ExporterFromList: global platforms platforms = Platforms() def __init__(self, titlesfile=None, platform=None, region=None): # set default encoding to utf8 for parsing and logging # utf-8 characters in console and files # reload(sys) sys.setdefaultencoding('utf8') configure_logging(install_root_handler=False) logging.basicConfig(filename='export.log', filemode='a', format='%(levelname)s: %(message)s', level=logging.INFO) # identify platform # self.platform = platform if self.platform is None: logging.error('No platform found! Pass it as an argument.') return else: platformId = platforms.getId(self.platform) if platformId is None: logging.error('Platform ' + self.platform + ' not supported.') return self.titlesfile = titlesfile self.region = region if self.region is None: self.region = "Worldwide" if titlesfile: titles = [] urls = [] with open(self.titlesfile) as f: titles = f.read().splitlines() for title in titles: logging.debug('Submitting title:' + title) urls.append('http://mobygames.com/search/quick' + '?q=' + title + '&p=' + platformId + '&search=Go' '&sFilter=1' '&sG=on' '&search_title=' + urllib.quote(title) + '&search_platform=' + urllib.quote(self.platform) + '&search_region=' + urllib.quote(self.region)) process = CrawlerProcess(get_project_settings()) process.crawl(MobygamesSpider, start_urls=urls) process.start() else: logging.warning('No file.') # strip all text in parentheses # def normalizeName(self, value): value = self.remove_parentheses(value) value = self.remove_brackets(value) return value.strip() # remove parentheses and text inside them def remove_parentheses(self, x): if '(' in x and ')' in x: x = re.sub('\(.*?\)', '', x) return x # remove brackets and text inside them def remove_brackets(self, x): if '[' in x and ']' in x: x = re.sub('\[.*?\]', '', x) return x