241 lines
5.7 KiB
Python
241 lines
5.7 KiB
Python
|
#!/usr/bin/env python
|
||
|
|
||
|
import serial
|
||
|
import sensors
|
||
|
import os
|
||
|
import re
|
||
|
import subprocess
|
||
|
import time
|
||
|
import signal
|
||
|
|
||
|
running = True
|
||
|
|
||
|
# Be careful if you use Python 3, it'll send Unicode and
|
||
|
# the Arduino won't know what to do with it. Add a b before
|
||
|
# the strings or use the conversion functions before sending
|
||
|
|
||
|
def signal_handler(signal, frame):
|
||
|
global running
|
||
|
print("Exiting...")
|
||
|
running = False
|
||
|
|
||
|
|
||
|
def available_cpu_count():
|
||
|
""" Number of available virtual or physical CPUs on this system, i.e.
|
||
|
user/real as output by time(1) when called with an optimally scaling
|
||
|
userspace-only program"""
|
||
|
|
||
|
# cpuset
|
||
|
# cpuset may restrict the number of *available* processors
|
||
|
try:
|
||
|
m = re.search(r'(?m)^Cpus_allowed:\s*(.*)$',
|
||
|
open('/proc/self/status').read())
|
||
|
if m:
|
||
|
res = bin(int(m.group(1).replace(',', ''), 16)).count('1')
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except IOError:
|
||
|
pass
|
||
|
|
||
|
# Python 2.6+
|
||
|
try:
|
||
|
import multiprocessing
|
||
|
return multiprocessing.cpu_count()
|
||
|
except (ImportError, NotImplementedError):
|
||
|
pass
|
||
|
|
||
|
# http://code.google.com/p/psutil/
|
||
|
try:
|
||
|
import psutil
|
||
|
return psutil.cpu_count() # psutil.NUM_CPUS on old versions
|
||
|
except (ImportError, AttributeError):
|
||
|
pass
|
||
|
|
||
|
# POSIX
|
||
|
try:
|
||
|
res = int(os.sysconf('SC_NPROCESSORS_ONLN'))
|
||
|
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except (AttributeError, ValueError):
|
||
|
pass
|
||
|
|
||
|
# Windows
|
||
|
try:
|
||
|
res = int(os.environ['NUMBER_OF_PROCESSORS'])
|
||
|
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except (KeyError, ValueError):
|
||
|
pass
|
||
|
|
||
|
# jython
|
||
|
try:
|
||
|
from java.lang import Runtime
|
||
|
runtime = Runtime.getRuntime()
|
||
|
res = runtime.availableProcessors()
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except ImportError:
|
||
|
pass
|
||
|
|
||
|
# BSD
|
||
|
try:
|
||
|
sysctl = subprocess.Popen(['sysctl', '-n', 'hw.ncpu'],
|
||
|
stdout=subprocess.PIPE)
|
||
|
scStdout = sysctl.communicate()[0]
|
||
|
res = int(scStdout)
|
||
|
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except (OSError, ValueError):
|
||
|
pass
|
||
|
|
||
|
# Linux
|
||
|
try:
|
||
|
res = open('/proc/cpuinfo').read().count('processor\t:')
|
||
|
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except IOError:
|
||
|
pass
|
||
|
|
||
|
# Solaris
|
||
|
try:
|
||
|
pseudoDevices = os.listdir('/devices/pseudo/')
|
||
|
res = 0
|
||
|
for pd in pseudoDevices:
|
||
|
if re.match(r'^cpuid@[0-9]+$', pd):
|
||
|
res += 1
|
||
|
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except OSError:
|
||
|
pass
|
||
|
|
||
|
# Other UNIXes (heuristic)
|
||
|
try:
|
||
|
try:
|
||
|
dmesg = open('/var/run/dmesg.boot').read()
|
||
|
except IOError:
|
||
|
dmesgProcess = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
|
||
|
dmesg = dmesgProcess.communicate()[0]
|
||
|
|
||
|
res = 0
|
||
|
while '\ncpu' + str(res) + ':' in dmesg:
|
||
|
res += 1
|
||
|
|
||
|
if res > 0:
|
||
|
return res
|
||
|
except OSError:
|
||
|
pass
|
||
|
|
||
|
raise Exception('Can not determine number of CPUs on this system')
|
||
|
|
||
|
|
||
|
class Data(object):
|
||
|
"""
|
||
|
Data encapsulation
|
||
|
"""
|
||
|
def __init__(self, label, value):
|
||
|
self.label = label
|
||
|
if (label is not None):
|
||
|
self.value = int(value)
|
||
|
|
||
|
else:
|
||
|
self.value = value
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.label + " {}".format(self.value)
|
||
|
|
||
|
|
||
|
class Sensors(object):
|
||
|
"""
|
||
|
Parses the output of the sensors command
|
||
|
"""
|
||
|
def __init__(self):
|
||
|
print("Getting sensors ready")
|
||
|
sensors.init()
|
||
|
self.data = {}
|
||
|
self.cpus = available_cpu_count()
|
||
|
|
||
|
self.update()
|
||
|
|
||
|
def update(self):
|
||
|
print("Reading sensors")
|
||
|
cpu_counter = 0
|
||
|
fan_count = 0
|
||
|
|
||
|
for chip in sensors.iter_detected_chips():
|
||
|
for feature in chip:
|
||
|
if feature.get_value() > 0 and feature.label != 'CPUTIN':
|
||
|
if 'cpu' in feature.label.lower() or 'core' in feature.label.lower() and cpu_counter < self.cpus:
|
||
|
self.data['CPU{}'.format(cpu_counter)] = Data(feature.label, feature.get_value())
|
||
|
cpu_counter += 1
|
||
|
|
||
|
elif 'fan' in feature.label.lower():
|
||
|
self.data['FAN{}'.format(fan_count)] = Data(feature.label, feature.get_value())
|
||
|
fan_count += 1
|
||
|
|
||
|
elif 'sys' in feature.label.lower():
|
||
|
self.data['SYSTEM'] = Data(feature.label, feature.get_value())
|
||
|
|
||
|
|
||
|
class Arduino(object):
|
||
|
"""
|
||
|
With this class we communicate with the Arduino
|
||
|
"""
|
||
|
def __init__(self):
|
||
|
# TODO: Should be configurable or read from command line
|
||
|
print("Connecting to Arduino")
|
||
|
self.serial = serial.Serial('/dev/ttyACM0', 9600)
|
||
|
time.sleep(2)
|
||
|
|
||
|
def close(self):
|
||
|
self.serial.close()
|
||
|
|
||
|
def send(self, sensors_data):
|
||
|
print("Sending data")
|
||
|
sensors_data.update()
|
||
|
data = sensors_data.data
|
||
|
|
||
|
stream = []
|
||
|
|
||
|
keys = data.keys()
|
||
|
keys.sort()
|
||
|
|
||
|
for key in keys:
|
||
|
if 'CPU' in key:
|
||
|
stream.append("CPU={}".format(data[key]))
|
||
|
|
||
|
if 'FAN' in key:
|
||
|
stream.append("FAN={}".format(data[key]))
|
||
|
|
||
|
if 'SYS' in key:
|
||
|
stream.append("SYS={}".format(data[key]))
|
||
|
|
||
|
print("Sending {} datagrams".format(len(stream)))
|
||
|
|
||
|
res = ";".join(stream)
|
||
|
|
||
|
print(res)
|
||
|
|
||
|
self.serial.write(res + "\n")
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
signal.signal(signal.SIGINT, signal_handler)
|
||
|
arduino = Arduino()
|
||
|
sensor_data = Sensors()
|
||
|
|
||
|
while running:
|
||
|
arduino.send(sensor_data)
|
||
|
# This should be configurable too
|
||
|
time.sleep(3)
|
||
|
|
||
|
arduino.close()
|
||
|
|
||
|
|
||
|
|
||
|
|