@@ -118,6 +118,7 @@ def init_menu(self):
118118 restart_menu = selected_menu .addMenu ("Restart service" )
119119 for name in ("chrony" , "ros" , "swarm" ):
120120 restart_menu .addAction (name , lambda checked = False , n = name : self .restart_service (n ))
121+ selected_menu .addAction ("Update (git pull + restart)" , self .update_selected_drones )
121122 selected_menu .addAction ("Reload animation" , lambda : self .send_to_selected ("reload_animation" ))
122123 selected_menu .addAction ("Reboot" , self .reboot_selected )
123124
@@ -663,6 +664,30 @@ def restart_service(self, name):
663664 self .network .broadcast_command (selected , "restart_service" , {"name" : name })
664665 self .log (f"> restart { name } : { ', ' .join (selected )} " )
665666
667+ # Uses run_command only, so drones running an older client can be updated too.
668+ UPDATE_COMMAND = (
669+ "cd /home/pi/DroneSwarm && git -c safe.directory='*' pull --ff-only; rc=$?; "
670+ "if [ $rc -eq 0 ]; then setsid sh -c 'sleep 3; sudo -n systemctl restart droneswarm' "
671+ ">/dev/null 2>&1 < /dev/null & fi; exit $rc"
672+ )
673+
674+ def update_selected_drones (self ):
675+ selected = self ._require_selected ()
676+ if not selected :
677+ return
678+ armed = [c for c in selected if self .drones .get (c , {}).get ("armed" )]
679+ targets = [c for c in selected if c not in armed ]
680+ if armed :
681+ self .log (f"Update пропущен (armed): { ', ' .join (armed )} " )
682+ if not targets :
683+ return
684+ reply = QMessageBox .question (self , "Обновление" ,
685+ f"git pull и перезапуск клиента на { len (targets )} дронах?" ,
686+ QMessageBox .Yes | QMessageBox .No )
687+ if reply == QMessageBox .Yes :
688+ self .network .broadcast_command (targets , "run_command" , {"command" : self .UPDATE_COMMAND })
689+ self .log (f"> update: { ', ' .join (targets )} " )
690+
666691 def reboot_selected (self ):
667692 selected = self ._require_selected ()
668693 if not selected :
0 commit comments