Skip to content

Commit 92fbbb7

Browse files
committed
Update main.py
1 parent f949be5 commit 92fbbb7

File tree

1 file changed

+94
-128
lines changed

1 file changed

+94
-128
lines changed

main.py

Lines changed: 94 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -2,149 +2,115 @@
22
import psutil
33
import subprocess
44
import sys
5-
from threading import Thread
5+
from threading import Thread, Lock
66
import time
7-
# from plant_classification import read_v
87
import Jetson.GPIO as GPIO
9-
# except (RuntimeError, ModuleNotFoundError):
10-
# class MockGPIO:
11-
# BCM = BOARD = IN = OUT = HIGH = LOW = None
12-
# def setmode(self, mode): pass
13-
# def setup(self, pin, mode): pass
14-
# def output(self, pin, state): pass
15-
# def input(self, pin): return False
16-
# def cleanup(self): pass
17-
# def wait_for_edge(self, pin, edge_type):
18-
# print(f"Simulating waiting for {edge_type} edge on pin {pin}")
19-
# return True
20-
# GPIO = MockGPIO()
8+
import logging
219

22-
def kill_python_scripts_by_name(target_names): # ex ['lighting_rainy.py', 'lighting_summer.py']
23-
"""
24-
Kill all running Python scripts whose command lines include one of the target names.
25-
Does not kill the currently running script.
26-
"""
27-
current_pid = os.getpid()
28-
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
29-
try:
30-
if proc.info['pid'] == current_pid:
31-
continue
32-
cmdline = proc.info['cmdline']
33-
if cmdline and 'python' in cmdline[0].lower():
34-
for name in target_names:
35-
if any(name in part for part in cmdline[1:]): # check script path/args
36-
print(f"Killing PID {proc.info['pid']}: {' '.join(cmdline)}")
37-
proc.kill()
38-
break
10+
# Configure logging
11+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
12+
logger = logging.getLogger(__name__)
3913

40-
except (psutil.NoSuchProcess, psutil.AccessDenied):
41-
continue
14+
# GPIO Pin Configuration
15+
ONOFF_PIN = 16 # Physical 21
16+
SEASONS = {
17+
'rainy': {'pin': 19, 'script': 'rainy_sound.py'}, # Physical 35
18+
'spring': {'pin': 8, 'script': 'spring_sound.py'}, # Physical 24
19+
'winter': {'pin': 4, 'script': 'winter_sound.py'} # Physical 7
20+
}
4221

43-
def run_script(script_name): # ex 'lighting_rainy.py'
44-
script_path = os.path.join(os.path.dirname(__file__), script_name)
45-
print(f"Running script: {script_path}")
46-
subprocess.Popen([sys.executable, script_path])
47-
48-
GPIO.setwarnings(False)
49-
GPIO.cleanup()
50-
GPIO.setmode(GPIO.BCM)
51-
52-
winter_pin = 4 #physical 7
53-
spring_pin = 8 #physical 24
54-
rainy_pin = 19 #physical 35
22+
running_processes = []
23+
process_lock = Lock()
5524

56-
GPIO.setup(rainy_pin, GPIO.IN) # button pin set as input
57-
GPIO.setup(spring_pin, GPIO.IN) # button pin set as input
58-
GPIO.setup(winter_pin, GPIO.IN) # button pin set as input
25+
def kill_python_scripts_by_name(target_names):
26+
"""Kill running Python scripts matching target names."""
27+
with process_lock:
28+
for proc in running_processes[:]:
29+
try:
30+
if proc.poll() is None:
31+
cmdline = proc.cmdline()
32+
for name in target_names:
33+
if any(name in part for part in cmdline[1:]):
34+
logger.info(f"Terminating PID {proc.pid}: {' '.join(cmdline)}")
35+
proc.terminate()
36+
try:
37+
proc.wait(timeout=2)
38+
except subprocess.TimeoutExpired:
39+
logger.warning(f"Force killing PID {proc.pid}")
40+
proc.kill()
41+
running_processes.remove(proc)
42+
break
43+
except (psutil.NoSuchProcess, psutil.AccessDenied):
44+
running_processes.remove(proc)
5945

60-
ss_old = ''
61-
ss_new = 'spring'
46+
def run_script(script_name):
47+
"""Run a Python script in a subprocess and track its process."""
48+
script_path = os.path.join(os.path.dirname(__file__), script_name)
49+
if not os.path.exists(script_path):
50+
logger.error(f"Script {script_path} not found.")
51+
return None
52+
try:
53+
logger.info(f"Running script: {script_path}")
54+
proc = subprocess.Popen([sys.executable, script_path])
55+
with process_lock:
56+
running_processes.append(proc)
57+
return proc
58+
except Exception as e:
59+
logger.error(f"Failed to run {script_path}: {e}")
60+
return None
6261

6362
class choose_season(Thread):
63+
"""Thread to handle season selection based on GPIO button presses."""
6464
def __init__(self):
6565
Thread.__init__(self)
6666
self.running = True
67+
self.ss_old = ''
68+
self.ss_new = 'spring'
69+
for season, config in SEASONS.items():
70+
GPIO.add_event_detect(
71+
config['pin'], GPIO.RISING, callback=self.handle_button, bouncetime=200
72+
)
73+
74+
def handle_button(self, pin):
75+
for season, config in SEASONS.items():
76+
if pin == config['pin']:
77+
self.ss_new = season
78+
logger.info(f"{season.capitalize()} season selected.")
79+
if self.ss_new != self.ss_old:
80+
self.ss_old = self.ss_new
81+
kill_python_scripts_by_name([config['script'] for config in SEASONS.values()])
82+
run_script(SEASONS[self.ss_new]['script'])
83+
self.ss_new = ''
84+
break
85+
6786
def stop(self):
6887
self.running = False
69-
def run(self):
70-
global ss_old, ss_new
71-
while self.running:
72-
while len(ss_new) == 0:
73-
if GPIO.input(rainy_pin) == GPIO.HIGH:
74-
ss_new = 'rainy'
75-
print("Rainy season selected.")
76-
break
77-
elif GPIO.input(spring_pin) == GPIO.HIGH:
78-
ss_new = 'spring'
79-
print("spring season selected.")
80-
break
81-
elif GPIO.input(winter_pin) == GPIO.HIGH:
82-
ss_new = 'winter'
83-
print("Winter season selected.")
84-
break
85-
time.sleep(0.1)
86-
87-
if ss_new == ss_old:
88-
pass
89-
else:
90-
ss_old = ss_new[:]
91-
kill_python_scripts_by_name(['spring_sound.py', 'rainy_sound.py','winter_sound.py'])
92-
if ss_new == 'rainy':
93-
run_script('rainy_sound.py')
94-
elif ss_new == 'spring':
95-
run_script('spring_sound.py')
96-
elif ss_new == 'winter':
97-
run_script('winter_sound.py')
98-
else:
99-
pass
100-
ss_new = ''
88+
for season, config in SEASONS.items():
89+
GPIO.remove_event_detect(config['pin'])
10190

10291
if __name__ == '__main__':
103-
onoff_pin = 9 #physical 21
104-
GPIO.setmode(GPIO.BCM)
105-
GPIO.setup(onoff_pin, GPIO.IN)
106-
target_scripts = ['playsound.py','plant_classification.py','spring_sound.py','rainy_sound.py','winter_sound.py']
107-
while True:
108-
GPIO.wait_for_edge(onoff_pin, GPIO.RISING)
109-
print("ON button pressed. Starting system...")
110-
time.sleep(0.3)
111-
choose_season_thread = choose_season()
112-
choose_season_thread.start()
113-
run_script('playsound.py')
114-
GPIO.wait_for_edge(onoff_pin, GPIO.RISING)
115-
print("OFF button pressed. Stopping system...")
116-
time.sleep(0.3)
117-
choose_season_thread.stop()
118-
ss_old = ''
119-
ss_new = 'spring'
120-
kill_python_scripts_by_name(target_scripts)
121-
122-
# if __name__ == '__main__':
123-
# onoff_pin = 7 # physical 26
124-
# GPIO.setmode(GPIO.BCM)
125-
# GPIO.setup(onoff_pin, GPIO.IN)
126-
# target_scripts = ['playsound.py', 'plant_classification', 'spring_sound.py', 'rainy_sound.py', 'winter_sound.py']
127-
# run_script('playsound.py')
128-
129-
# while True:
130-
# GPIO.wait_for_edge(onoff_pin, GPIO.RISING)
131-
# print("ON button pressed. Starting system...")
132-
# time.sleep(0.3)
133-
134-
# choose_season_thread = choose_season()
135-
# choose_season_thread.start()
92+
GPIO.setwarnings(False)
93+
GPIO.setmode(GPIO.BCM)
94+
GPIO.setup(ONOFF_PIN, GPIO.IN)
95+
for season, config in SEASONS.items():
96+
GPIO.setup(config['pin'], GPIO.IN)
13697

137-
# try:
138-
# while GPIO.input(onoff_pin) == GPIO.LOW:
139-
# voltages = read_v()
140-
# voltage_str = ' | '.join([f"CH{i}: {v:.2f} V" for i, v in enumerate(voltages)])
141-
# print("Voltages ->", voltage_str)
142-
# time.sleep(0.5)
143-
# except KeyboardInterrupt:
144-
# break
98+
target_scripts = ['playsound.py', 'plant_classification.py', 'spring_sound.py', 'rainy_sound.py', 'winter_sound.py']
14599

146-
# print("OFF button pressed. Stopping system...")
147-
# choose_season_thread.stop()
148-
# ss_old = ''
149-
# ss_new = 'spring'
150-
# kill_python_scripts_by_name(target_scripts)
100+
try:
101+
while True:
102+
GPIO.wait_for_edge(ONOFF_PIN, GPIO.RISING)
103+
logger.info("ON button pressed. Starting system...")
104+
time.sleep(0.3)
105+
choose_season_thread = choose_season()
106+
choose_season_thread.start()
107+
run_script('playsound.py')
108+
GPIO.wait_for_edge(ONOFF_PIN, GPIO.RISING)
109+
logger.info("OFF button pressed. Stopping system...")
110+
time.sleep(0.3)
111+
choose_season_thread.stop()
112+
choose_season_thread.join()
113+
kill_python_scripts_by_name(target_scripts)
114+
finally:
115+
GPIO.cleanup()
116+
logger.info("GPIO resources cleaned up.")

0 commit comments

Comments
 (0)