From b6c91e4ac470abe849c61e805328ff652be3e13e Mon Sep 17 00:00:00 2001 From: YuruC3 Date: Wed, 2 Jul 2025 21:26:06 +0200 Subject: [PATCH] working with influxdb implementation --- PC_CONTROL_CODE/dockerInflux/Dockerfile | 28 ++ .../dockerInflux/docker-compose.yml | 19 ++ PC_CONTROL_CODE/dockerInflux/influxSend.py | 54 ++++ PC_CONTROL_CODE/dockerInflux/mainDocker.py | 273 ++++++++++++++++++ PC_CONTROL_CODE/dockerInflux/md1200.env | 14 + 5 files changed, 388 insertions(+) create mode 100644 PC_CONTROL_CODE/dockerInflux/Dockerfile create mode 100644 PC_CONTROL_CODE/dockerInflux/docker-compose.yml create mode 100644 PC_CONTROL_CODE/dockerInflux/influxSend.py create mode 100644 PC_CONTROL_CODE/dockerInflux/mainDocker.py create mode 100644 PC_CONTROL_CODE/dockerInflux/md1200.env diff --git a/PC_CONTROL_CODE/dockerInflux/Dockerfile b/PC_CONTROL_CODE/dockerInflux/Dockerfile new file mode 100644 index 0000000..df75844 --- /dev/null +++ b/PC_CONTROL_CODE/dockerInflux/Dockerfile @@ -0,0 +1,28 @@ +FROM alpine:latest + +# https://docs.docker.com/reference/dockerfile/#environment-replacement +# ENV MD1200BAUD=38400 +# ENV SERIALADAPTER=/dev/ttyUSB0 +# ENV EPPYSLEEPY=300 + +# VOLUME [""] + +RUN apk update && \ + apk add python3 py3-pip + +RUN mkdir /etc/MD1200FAN/ +WORKDIR /etc/MD1200FAN/ + +COPY ./mainDocker.py /etc/MD1200FAN/ +# COPY ./requirements.txt /etc/MD1200FAN/ + +RUN python3 -m venv venv && \ + venv/bin/python3 -m pip install --upgrade pip && \ + venv/bin/pip3 install PySerial influxdb_client + # venv/bin/pip3 install -r requirements.txt + +# VOLUME ["/etc/MD1200FAN/"] + +CMD ["venv/bin/python3", "mainDocker.py"] + +# CMD ["/etc/MD1200FAN/venv/bin/python3", "/etc/MD1200FAN/mainDocker.py"] \ No newline at end of file diff --git a/PC_CONTROL_CODE/dockerInflux/docker-compose.yml b/PC_CONTROL_CODE/dockerInflux/docker-compose.yml new file mode 100644 index 0000000..df1e4de --- /dev/null +++ b/PC_CONTROL_CODE/dockerInflux/docker-compose.yml @@ -0,0 +1,19 @@ +--- + +services: + mdfanchanger: + container_name: MD_Fan_Changer + image: yuruc3/md1200_fan_controll:v1.2.1 + environment: + # - MD1200BAUD= + - SERIALADAPTER=/dev/ttyUSB0 + - TEMP_FACTOR=17 + - EPPYSLEEPY=0.5 + - MDSERIALTIMEOUT=0.5 + # - LOW_FAN_TRSHD= + # - HIGH_FAN_TRSHD= + env_file: "md1200.env" + devices: + - /dev/ttyUSB0:/dev/ttyUSB0 + restart: unless-stopped + privileged: false diff --git a/PC_CONTROL_CODE/dockerInflux/influxSend.py b/PC_CONTROL_CODE/dockerInflux/influxSend.py new file mode 100644 index 0000000..ce3986b --- /dev/null +++ b/PC_CONTROL_CODE/dockerInflux/influxSend.py @@ -0,0 +1,54 @@ +import time, os, influxdb_client +from influxdb_client import InfluxDBClient, Point, WritePrecision +from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions +from datetime import timedelta +from concurrent.futures import ThreadPoolExecutor + + +# INFLUXDB config +# token = "apg1gysUeCcxdcRTMmosJTenbEppmUNi9rXlANDB2oNadBdWAu2GVTDc_q_dyo0iyYsckKaOvPRm6ba2NK0y_A==" +token = os.getenv("INFLUX_TOKEN") +# bucket = "JBOD" +bucket = os.getenv("INFLUX_BUCKET") +# org = "staging" +org = os.getenv("INFLUX_ORG") +# url = "http://localhost:8086" +url = os.getenv("INFLUX_URL") +# measurement = "MD1200" +measurement = os.getenv("INFLUX_MEASUREMENT") +# MACHINE_TAG = "CHONGUS1200" +MACHINE_TAG = os.getenv("INFLUX_MACHINE_TAG") +# LOCATION = "HQ" +LOCATION = os.getenv("INFLUX_LOCATION") +# INFLX_SEPARATE_POINTS = 0.1 +INFLUX_SEPARATE_POINTS = int(os.getenv("INFLUX_SEPARATE_POINTS")) + +# Initialize InfluxDB client and influxdb API +inflxdb_client = influxdb_client.InfluxDBClient(url=url, token=token, org=org) +#write_api = inflxdb_client.write_api(write_options=SYNCHRONOUS) +write_api = inflxdb_client.write_api(write_options=WriteOptions(batch_size=500, flush_interval=1000)) + +# Threaded flow processor +def process_temps(inEntry): + global MDict + + # ---LeData--- + # {'bp1': 35, 'bp2': 29, 'sim0': 35, 'sim1': 34, 'exp0': 56, 'exp1': 54} + # ---LeData--- + + # Prep InfluxDB data + inflxdb_Data_To_Send = ( + influxdb_client.Point(f"{measurement}-script") + .tag("MACHINE", MACHINE_TAG) + .tag("LOCATION", LOCATION) + .field("Backplane1", inEntry["bp1"]) + .field("Backplane2", inEntry["bp2"]) + .field("SASIntModule0", inEntry["sim0"]) + .field("SASIntModule1", inEntry["sim1"]) + .field("Expander0", inEntry["exp0"]) + .field("Expander1", inEntry["exp1"]) + .field("Average", inEntry["avg"]) + ) + + print("----------------") + return () diff --git a/PC_CONTROL_CODE/dockerInflux/mainDocker.py b/PC_CONTROL_CODE/dockerInflux/mainDocker.py new file mode 100644 index 0000000..9a8c318 --- /dev/null +++ b/PC_CONTROL_CODE/dockerInflux/mainDocker.py @@ -0,0 +1,273 @@ +import time, os, influxdb_client, serial, threading +from influxdb_client import InfluxDBClient, Point, WritePrecision +from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions +from concurrent.futures import ThreadPoolExecutor + +# setting consts that can be customized + +# baud rate. Prob not needed as 38400 is standard +MD1200BAUD = int(os.getenv("MD1200BAUD", 38400)) +# used if you want to run it on multiple JBODs +SERIALADAPTER = os.getenv("SERIALADAPTER", "/dev/ttyUSB0") +# Factor that defines how aggressive the temperature curve is +TEMP_FACTOR = int(os.getenv("TEMP_FACTOR", 16)) +# time between sending command to get temp and storing it. It's there to allow JBOD to answer +EPPYSLEEPY = float(os.getenv("EPPYSLEEPY", 1)) + +LOW_FAN_TRSHD = int(os.getenv("LOW_FAN_TRSHD", 21)) +HIGH_FAN_TRSHD = int(os.getenv("HIGH_FAN_TRSHD", 40)) + +GETTMPCMND = os.getenv("GETTMPCMND", "_temp_rd") +SETFANCMND = os.getenv("SETFANCMND", "set_speed") + +DEFOUTPRCNTG = int(os.getenv("DEFOUTPRCNTG", 24)) + +MDSERIALTIMEOUT = float(os.getenv("MDSERIALTIMEOUT", 1)) + +TEMPREADINTERVAL = float(os.getenv("TEMPREADINTERVAL", 15)) + +# INFLUXDB config +# token = "apg1gysUeCcxdcRTMmosJTenbEppmUNi9rXlANDB2oNadBdWAu2GVTDc_q_dyo0iyYsckKaOvPRm6ba2NK0y_A==" +token = os.getenv("INFLUX_TOKEN") +# bucket = "JBOD" +bucket = os.getenv("INFLUX_BUCKET") +# org = "staging" +org = os.getenv("INFLUX_ORG") +# url = "http://localhost:8086" +url = os.getenv("INFLUX_URL") +# measurement = "MD1200" +measurement = os.getenv("INFLUX_MEASUREMENT") +# MACHINE_TAG = "CHONGUS1200" +MACHINE_TAG = os.getenv("INFLUX_MACHINE_TAG") +# LOCATION = "HQ" +LOCATION = os.getenv("INFLUX_LOCATION") +# INFLX_SEPARATE_POINTS = 0.1 +# INFLUX_SEPARATE_POINTS = float(os.getenv("INFLUX_SEPARATE_POINTS"), 0.1) + +# init +MDserial = serial.Serial( + port=SERIALADAPTER,\ + baudrate=MD1200BAUD,\ + parity=serial.PARITY_NONE,\ + stopbits=serial.STOPBITS_ONE,\ + bytesize=serial.EIGHTBITS,\ + timeout=MDSERIALTIMEOUT) + +lastTempReading = time.time() +MDtempDict = {} +MDict = {} +fluxSending = False +currentTime = 0 +lastTempReading = 0 +# Initialize InfluxDB client and influxdb API +# ---------------------UNCOMMENT----------------------- +# inflxdb_client = influxdb_client.InfluxDBClient(url=url, token=token, org=org) +# write_api = inflxdb_client.write_api(write_options=SYNCHRONOUS) +# ---------------------UNCOMMENT----------------------- + + +def getTemp(): + global MDict, fluxSending + + MDserial.write(f"{GETTMPCMND}\n\r".encode()) + time.sleep(1) + MDreturning = MDserial.read_until(" >").decode() + + # MDict = {} + + # Sanitise output + MDsanit = MDreturning.splitlines() + + #if there is smth do smth + if MDreturning: + + for line in MDsanit: + + if ">" in line or "b'" in line: + continue + + matchstm = line[2:6] + + match matchstm: + case "BP_1": + MDict["bp1"] = int(line[12:14]) + case "BP_2": + MDict["bp2"] = int(line[12:14]) + case "SIM0": + MDict["sim0"] = int(line[12:14]) + case "SIM1": + MDict["sim1"] = int(line[12:14]) + case "EXP0": + MDict["exp0"] = int(line[12:14]) + case "EXP1": + MDict["exp1"] = int(line[12:14]) + # case "AVG": + # MDict["avg"] = int(line[12:14]) + # MDict["avg"] = int(line.strip().split("=")[1].strip().replace("c", "")) + # try: + # # Extract number from e.g. ' AVG = 40c' + # temp = int(line.strip().split("=")[1].strip().replace("c", "")) + # MDict["avg"] = temp + # except Exception as e: + # # print(f"[WARN] Failed to parse AVG line: {line} ({e})", flush=True) + # pass + case _: + # try to catch the AVG line like: " AVG = 40c" + stripped = line.strip() + if stripped.startswith("AVG"): + try: + temp = int(stripped.split("=")[1].strip().replace("c", "")) + MDict["avg"] = temp + except Exception as e: + print(f"[WARN] Could not parse AVG line: {line} ({e})", flush=True) + continue + # continue + + + # {'bp1': 35, 'bp2': 29, 'sim0': 35, 'sim1': 33, 'exp0': 56, 'exp1': 54, 'avg': 40} + # process_temps(MDict) + fluxSending = True + + return MDict + + +def setSpeed(inSpeeDict: dict): + + bpavrg = 0 + # default + outfanprcntg = 0 + + # get backplanbe average + if "bp1" in inSpeeDict and "bp2" in inSpeeDict: + bpavrg = (inSpeeDict["bp1"] + inSpeeDict["bp2"]) /2 + outfanprcntg = int((bpavrg / (HIGH_FAN_TRSHD - LOW_FAN_TRSHD)) * TEMP_FACTOR) + # os.system(f"echo setting {outfanprcntg}%") + + # Set fan speed + if outfanprcntg >= 20: + MDserial.write((f"{SETFANCMND} {str(outfanprcntg)} \n\r").encode()) + print(f"setting {outfanprcntg}%", flush=True) + return 0 + else: + # Set default value + MDserial.write((f"{SETFANCMND} {str(DEFOUTPRCNTG)} \n\r").encode()) + return 1 + + # If something goes super wrong + return -1 + + + +# Check if UART is used +# Not neede because when defining MDserial it gets automatically opened +# Will leave it here anyway +# try: +# MDserial.open() +# except serial.serialutil.SerialException: +# # MDserial.close() +# # MDserial.open() +# print("Port allready opened.\nTry closing it first") + +# Threaded flow processor +def process_temps(): + global MDict, fluxSending + + while True: + # ---LeData--- + # {'bp1': 35, 'bp2': 29, 'sim0': 35, 'sim1': 34, 'exp0': 56, 'exp1': 54} + # ---LeData--- + if fluxSending: + # Prep InfluxDB data + # ---------------------UNCOMMENT----------------------- + # inflxdb_Data_To_Send = ( + # influxdb_client.Point(f"{measurement}-script") + # .tag("MACHINE", MACHINE_TAG) + # .tag("LOCATION", LOCATION) + # .field("Backplane1", MDict["bp1"]) + # .field("Backplane2", MDict["bp2"]) + # .field("SASIntModule0", MDict["sim0"]) + # .field("SASIntModule1", MDict["sim1"]) + # .field("Expander0", MDict["exp0"]) + # .field("Expander1", MDict["exp1"]) + # .field("Average", MDict["avg"]) + # ) + + # inflxdb_Datazz_To_Send.append(inflxdb_Data_To_Send) + # # Send data to InfluxDB + # write_api.write(bucket=bucket, org=org, record=inflxdb_Data_To_Send) + # # time.sleep(INFLX_SEPARATE_POINTS) # separate points + + # # print(f"{len(bigDict)} <--- This many entrys") + + + # # Clean up before another loop + # bigDict.clear() + # inflxdb_Datazz_To_Send.clear() + # ---------------------UNCOMMENT----------------------- + print("I'm sending stuff to InfluxDB") + + fluxSending = False + + + # print("----------------") + # return () + else: + continue + + + + +# Init +MDict = getTemp() +lastTempReading = time.time() + +def mainCodeHere(): + while True: + global MDict, fluxSending, currentTime, lastTempReading + # https://stackoverflow.com/questions/52578122/not-able-to-send-the-enter-command-on-pyserial + + # get temperature data, wait for MD1200 to answer and store + + currentTime = time.time() + + if currentTime - lastTempReading >= TEMPREADINTERVAL: + getTemp() + lastTempReading = currentTime + + if MDict: + setSpeedrcode = setSpeed(MDict) + + # good + if setSpeedrcode == 0: + pass + # print("Were mint", flush=True) + # time.sleep(EPPYSLEEPY) + # not good + elif setSpeedrcode == 1: + print("Ambigous temperature readings.\nFalling back to safe values.", flush=True) + # time.sleep(EPPYSLEEPY) + # very not good + elif setSpeedrcode == -1: + print("o nyo", flush=True) + exit() + # very very very not good + else: + print("idk", flush=True) + exit() + else: + print(f"temperature not yet pulled.\nFalling back do default fan speed", flush=True) + # os.system(f"echo temperature not yet pulled.\nFalling back do default fan speed") + MDserial.write((f"{SETFANCMND} {str(DEFOUTPRCNTG)} \n\r").encode()) + + time.sleep(EPPYSLEEPY) + + +# Prepare threads and launch them +thread_main = threading.Thread(target=mainCodeHere) +thread_flux = threading.Thread(target=process_temps) + +thread_main.start() +thread_flux.start() + +thread_main.join() +thread_flux.join() \ No newline at end of file diff --git a/PC_CONTROL_CODE/dockerInflux/md1200.env b/PC_CONTROL_CODE/dockerInflux/md1200.env new file mode 100644 index 0000000..fcd2d78 --- /dev/null +++ b/PC_CONTROL_CODE/dockerInflux/md1200.env @@ -0,0 +1,14 @@ +# baud_rate= +# serial_adapter= +# wait_time= +# temp_factor= +# lower_treshold= +# upper_treshold= +INFLUX_TOKEN=apg1gysUeCcxdcRTMmosJTenbEppmUNi9rXlANDB2oNadBdWAu2GVTDc_q_dyo0iyYsckKaOvPRm6ba2NK0y_A== +INFLUX_BUCKET=JBOD +INFLUX_ORG=staging +INFLUX_URL=http://localhost:8086 +INFLUX_MEASUREMENT=MD1200 +INFLUX_MACHINE_TAG=CHONGUS1200 +INFLUX_LOCATION=HQ +INFLX_SEPARATE_POINTS=0.1 \ No newline at end of file