import json import time import sys, signal import gpiod from pymodbus.client import ModbusTcpClient # import AWSIoTPythonSDK.MQTTLib as AWSIoTPyMQTT import sdtcloudnodeqmqtt import pytz from datetime import datetime import threading, socket import uuid import logging from logging.handlers import RotatingFileHandler def Motor(chip, status, action): if action == 'On': status[0] = 1 else: # action == 'Off' status[0] = 0 chip.set_values(status) def Valve_Vent(chip, status, action): if action == 'On': status[1] = 1 else: # action == 'Off' status[1] = 0 chip.set_values(status) def Valve_MixedWater(chip, status, action): if action == 'On': status[2] = 1 else: # action == 'Off' status[2] = 0 chip.set_values(status) def Valve_PureWater(chip, status, action, duration=7): global pure_valve_status status[2] = 0 status[3] = 0 chip.set_values(status) time.sleep(0.05) if pure_valve_status != 0 and action == 'Off': status[2] = 0 status[3] = 1 chip.set_values(status) time.sleep(7) pure_valve_status = 0 elif pure_valve_status == 0 and action == 'On': status[2] = 1 status[3] = 0 chip.set_values(status) time.sleep(duration) if duration >= 7: pure_valve_status = 2 elif duration < 7: pure_valve_status = 1 status[2] = 0 status[3] = 0 chip.set_values(status) time.sleep(0.05) # def Valve_EnterWater(chip, status, action): def Valve_EnterWater(chip, status, action, duration=7): # if action == 'On': # status[4] = 1 # else: # action == 'Off' # status[4] = 0 # chip.set_values(status) global main_valve_status status[5] = 0 status[6] = 0 chip.set_values(status) time.sleep(0.05) if main_valve_status != 0 and action == 'Off': status[5] = 0 status[6] = 1 chip.set_values(status) time.sleep(7) main_valve_status = 0 elif main_valve_status == 0 and action == 'On': status[5] = 1 status[6] = 0 chip.set_values(status) time.sleep(duration) if duration >= 7: main_valve_status = 2 elif duration < 7: main_valve_status = 1 status[5] = 0 status[6] = 0 chip.set_values(status) time.sleep(0.05) # def Valve_MainWater(chip, status, action, duration=7): def Valve_MainWater(chip, status, action): global main_valve_status # status[5] = 0 # status[6] = 0 # chip.set_values(status) # time.sleep(0.05) # if main_valve_status != 0 and action == 'Off': # status[5] = 0 # status[6] = 1 # chip.set_values(status) # time.sleep(7) # main_valve_status = 0 # elif main_valve_status == 0 and action == 'On': # status[5] = 1 # status[6] = 0 # chip.set_values(status) # time.sleep(duration) # if duration >= 7: # main_valve_status = 2 # elif duration < 7: # main_valve_status = 1 # status[5] = 0 # status[6] = 0 # chip.set_values(status) # time.sleep(0.05) if action == 'On': status[4] = 1 else: # action == 'Off' status[4] = 0 chip.set_values(status) def Measure_Weight(client): # print('in') val = 0 try: result = client.read_holding_registers(1, 1) if not result: print(f'Error: {result}') else: val = result.registers[0] val -= 1000 val /= 1000 # print(f'value: {val}') except Exception as e: print(f'Measure_Weight Error: {e}') pass return float(val) def Calculate_Concentration(weight): global data, volume_water data['data']['weight'] = weight result = (float(weight) * volume_water * 128.5) - 126.11 # 1000 / 531 = 1.883239171 data['data']['concentration'] = result # print(f'{weight}, {result}') return result def Set_Zero(client): client.write_coil(1, 1) def Command_Read(): global client, main_valve_status, logger with open('./control.json', 'r') as f: cmd = json.load(f) if cmd['type'] == 'auto': main_duration = float(cmd['device']['main']['duration']) pure_duration = float(cmd['device']['pure']['duration']) step0_duration = float(cmd['working-time']['step0']) step1_duration = float(cmd['working-time']['step1']) step2_duration = float(cmd['working-time']['step2']) step3_duration = float(cmd['working-time']['step3']) step4_duration = float(cmd['working-time']['step4']) step5_duration = float(cmd['working-time']['step5']) step6_duration = float(cmd['working-time']['step6']) step7_duration = float(cmd['working-time']['step7']) step8_duration = float(cmd['working-time']['step8']) step9_duration = float(cmd['working-time']['step9']) # Step 0. Mesure init weight before starting the sequence time.sleep(step0_duration) start = Measure_Weight(client=client) # Step 1. Vent pured water before input mixed water # Target valve status: [Motor: Off, Vent: On, Pure: Off, Enter: On, Main: On] # Valve_EnterWater(chip=output_lines, status=status, action='On') Valve_MainWater(chip=output_lines, status=status, action='On') time.sleep(0.5) Valve_Vent(chip=output_lines, status=status, action='On') time.sleep(0.5) # Valve_MainWater(chip=output_lines, status=status, action='On', duration=main_duration) Valve_EnterWater(chip=output_lines, status=status, action='On', duration=main_duration) time.sleep(step1_duration) # Step 2. Empty the remaining pure water # Target valve status: [Motor: Off, Vent: On, Pure: Off, Enter: Off, Main: On] Valve_EnterWater(chip=output_lines, status=status, action='Off') time.sleep(step2_duration) # Step 3. Input the mixed water # Target valve status: [Motor: Off, Vent: Off, Pure: Off, Enter: On, Main: On] Valve_Vent(chip=output_lines, status=status, action='Off') time.sleep(0.5) # Valve_EnterWater(chip=output_lines, status=status, action='On') Valve_EnterWater(chip=output_lines, status=status, action='On', duration=main_duration) time.sleep(step3_duration) # Step 4. Mesure the weight # Target valve status: [Motor: Off, Vent: Off, Pure: Off, Enter: Off, Main: Off] Valve_EnterWater(chip=output_lines, status=status, action='Off') time.sleep(0.5) Valve_MainWater(chip=output_lines, status=status, action='Off') time.sleep(step4_duration) end = Measure_Weight(client=client) time.sleep(1) res = Calculate_Concentration(weight=(float(end)-float(start))) logger.debug(f'[auto] weight: {end - start} concentration: {res:.3f}') # Step 5. Drain the mixed water and add pure water. # Target valve status: [Motor: Off, Vent: On, Pure: On, Enter: On, Main: Off] Valve_EnterWater(chip=output_lines, status=status, action='On') time.sleep(0.5) Valve_Vent(chip=output_lines, status=status, action='On') time.sleep(0.5) # Valve_PureWater(chip=output_lines, status=status, action='On', duration=pure_duration) Valve_PureWater(chip=output_lines, status=status, action='On') time.sleep(step5_duration) # Step 6. Drain mixed water # Target valve status: [Motor: Off, Vent: On, Pure: On, Enter: Off, Main: Off] Valve_EnterWater(chip=output_lines, status=status, action='Off') time.sleep(step6_duration) # Step 7. Input pure water and clean # Target valve status: [Motor: On, Vent: Off, Pure: On, Enter: On, Main: Off] Valve_EnterWater(chip=output_lines, status=status, action='On') time.sleep(0.5) Valve_Vent(chip=output_lines, status=status, action='Off') time.sleep(0.5) Motor(chip=output_lines, status=status, action='On') time.sleep(step7_duration) # Step 8. Drain pure Water # Target valve status: [Motor: On, Vent: On, Pure: Off, Enter: Off, Main: Off] Valve_EnterWater(chip=output_lines, status=status, action='Off') time.sleep(0.5) Valve_Vent(chip=output_lines, status=status, action='On') time.sleep(0.5) Valve_PureWater(chip=output_lines, status=status, action='Off') time.sleep(step8_duration) # Step 9. Stop moter # Target valve status: [Motor: Off, Vent: On, Pure: Off, Enter: Off, Main: Off] Motor(chip=output_lines, status=status, action='Off') time.sleep(step9_duration) return 1 elif cmd['type'] == 'clean': logger.debug(f"[clean] duration: {int(cmd['maintenance']['clean']['duration'])}") clean_system() time.sleep(3) else: # cmd['type'] == 'manual' Motor(chip=output_lines, status=status, action=cmd['device']['motor']['action']) Valve_Vent(chip=output_lines, status=status, action=cmd['device']['vent']['action']) # Valve_MixedWater(chip=output_lines, status=status, action=cmd['device']['mixed']['action']) # Valve_PureWater(chip=output_lines, status=status, action=cmd['device']['pure']['action']) # Valve_EnterWater(chip=output_lines, status=status, action=cmd['device']['enter']['action']) Valve_MainWater(chip=output_lines, status=status, action=cmd['device']['main']['action']) if cmd['device']['pure']['duration'] == 0: Valve_PureWater(chip=output_lines, status=status, action=cmd['device']['pure']['action']) else: Valve_PureWater(chip=output_lines, status=status, action=cmd['device']['pure']['action'], duration=cmd['device']['pure']['duration']) # if cmd['device']['main']['duration'] == 0: # Valve_MainWater(chip=output_lines, status=status, action=cmd['device']['main']['action']) # else: # Valve_MainWater(chip=output_lines, status=status, action=cmd['device']['main']['action'], duration=cmd['device']['main']['duration']) if cmd['device']['main']['duration'] == 0: Valve_EnterWater(chip=output_lines, status=status, action=cmd['device']['enter']['action']) else: Valve_EnterWater(chip=output_lines, status=status, action=cmd['device']['enter']['action'], duration=cmd['device']['main']['duration']) if cmd['device']['measure']['action'] == 'On': result = Measure_Weight(client=client) Calculate_Concentration(result) return 1 if cmd['device']['setzero']['action'] == 'On': Set_Zero(client=client) return 0 def clean_system(): global main_valve_status with open('./control.json', 'r') as f: cmd = json.load(f) clean_duration = int(cmd['maintenance']['clean']['duration']) if cmd['type'] == 'clean': Valve_EnterWater(chip=output_lines, status=status, action='Off') time.sleep(0.5) Valve_MainWater(chip=output_lines, status=status, action='On') time.sleep(0.5) Valve_PureWater(chip=output_lines, status=status, action='On') time.sleep(clean_duration) Valve_MainWater(chip=output_lines, status=status, action='Off') time.sleep(0.5) Valve_EnterWater(chip=output_lines, status=status, action='On') time.sleep(0.5) Valve_Vent(chip=output_lines, status=status, action='On') time.sleep(clean_duration) Valve_PureWater(chip=output_lines, status=status, action='Off') time.sleep(0.5) Valve_Vent(chip=output_lines, status=status, action='Off') time.sleep(0.5) Valve_EnterWater(chip=output_lines, status=status, action='Off') time.sleep(0.5) def runAction(): # Write the app's actions in the "runAction" function. # Connect MQTT Broker # You have to rename client id. There are special rules. # Client Name: "device-app-*" # For Example # 1. device-app-test -> Good # 2. device-app-light-app -> Good # 3. device-test-app -> Bad global data sdtcloud = sdtcloudnodeqmqtt.sdtcloudnodeqmqtt() mqttClient1 = sdtcloud.setClient(f"device-app-1{uuid.uuid1()}") # parameter is client ID(string) mqttClient2 = sdtcloud.setClient(f"device-app-2{uuid.uuid1()}") # parameter is client ID(string) mqttClient3 = sdtcloud.setClient(f"device-app-3{uuid.uuid1()}") # parameter is client ID(string) mqttClient4 = sdtcloud.setClient(f"device-app-4{uuid.uuid1()}") # parameter is client ID(string) mqttClient5 = sdtcloud.setClient(f"device-app-5{uuid.uuid1()}") # parameter is client ID(string) mqttlist = [mqttClient1, mqttClient2, mqttClient3, mqttClient4, mqttClient5] # If you have config's value, please make config.json file. # - Project Code's variable: projectCode(string) # - Asset Code's variable: assetCode(string) # - You may need it to create a topic. cnt = 0 clean_flag = 0 while True: start = time.time() result = Command_Read() if result: data['timestamp'] = int(time.time() * 1000) sdtcloud.pubMessage(mqttlist[cnt], data) cnt += 1 if cnt == 5: cnt = 0 end = time.time() try: now = datetime.now(pytz.timezone('Asia/Seoul')) time_str = now.strftime('%H') time_int = int(time_str) with open('./control.json', 'r') as f: cmd = json.load(f) if time_int == int(cmd['maintenance']['clean']['time']): if clean_flag < 3: clean_flag += 1 clean_system() else: clean_flag = 0 except: pass diff = end - start if diff < 3: time.sleep(3 - diff) def handle_client(conn, ip, port): global data, logger while True: try: recv = conn.recv(100) if not recv: # print(f"Connection with {addr} was reset. Waiting for new connection.") break message = recv.decode().strip() if message[:3] != 'STX' or message[-3:] != 'ETX': err_msg = 'STXERRORETX' conn.sendall(err_msg.encode("utf8")) else: if message[3] == 'R': # Transfer data from SDT to Sampyo now = datetime.now(pytz.timezone('Asia/Seoul')) time_str = now.strftime('%Y%m%d%H%M%S') h_weight = float(data['data']['weight']) h_concentration = float(data['data']['concentration']) data_weight = '{:.3f}'.format(h_weight) data_concent = '{:.3f}'.format(h_concentration) logger.debug(f'TCP [R:Transfer data] weight: {data_weight}, concent: {data_concent}') send_msg = 'STX' + time_str + '|' + data_weight + '|' + data_concent + 'ETX' try: with open('./control.json', 'r') as f: cmd = json.load(f) cmd['device']['measure']['action'] = 'On' with open('./control.json', 'w') as f: json.dump(cmd, f, indent=4) conn.sendall(send_msg.encode("utf8")) except Exception as e: err_msg = 'STXERRORETX' conn.sendall(err_msg.encode("utf8")) elif message[3] == 'S': # Start measurement logger.debug(f'TCP [S:Start measurement]') try: with open('./control.json', 'r') as f: cmd = json.load(f) cmd['type'] = 'auto' with open('./control.json', 'w') as f: json.dump(cmd, f, indent=4) send_msg = 'STXOKETX' conn.sendall(send_msg.encode("utf8")) except Exception as e: err_msg = 'STXERRORETX' conn.sendall(err_msg.encode("utf8")) elif message[3] == 'C': # Clean sequence logger.debug(f'TCP [C:Clean sequence]') try: with open('./control.json', 'r') as f: cmd = json.load(f) cmd['type'] = 'clean' with open('./control.json', 'w') as f: json.dump(cmd, f, indent=4) send_msg = 'STXOKETX' conn.sendall(send_msg.encode("utf8")) except Exception as e: err_msg = 'STXERRORETX' conn.sendall(err_msg.encode("utf8")) elif message[3] == 'T': # Stop measurement logger.debug(f'TCP [T:Stop measurement]') try: with open('./control.json', 'r') as f: cmd = json.load(f) cmd['type'] = 'manual' cmd['device']['measure']['action'] = 'Off' with open('./control.json', 'w') as f: json.dump(cmd, f, indent=4) send_msg = 'STXOKETX' conn.sendall(send_msg.encode("utf8")) except Exception as e: err_msg = 'STXERRORETX' conn.sendall(err_msg.encode("utf8")) else: err_msg = 'STXERRORETX' conn.sendall(err_msg.encode("utf8")) except ConnectionResetError: # print("Connection with " + ip + ":" + port + " was reset. Waiting for new connection.") break # print("Closing the connection") def start_server(addr, port): host = addr # "172.17.16.201" port = port # 5000 soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: soc.bind((host, port)) except: sys.exit() soc.listen(1) # Only one connection at a time. while True: conn, addr = soc.accept() ip, port = str(addr[0]), str(addr[1]) print("Connected with " + ip + ":" + port) client_handler = threading.Thread(target=handle_client, args=(conn, ip, port)) client_handler.start() soc.close() def seoul_time(*args): utc_dt = datetime.now() seoul_tz = pytz.timezone('Asia/Seoul') return utc_dt.replace(tzinfo=pytz.utc).astimezone(seoul_tz).timetuple() def exit_handler(signum, frame): Motor(chip=output_lines, status=status, action='Off') Valve_Vent(chip=output_lines, status=status, action='Off') # Valve_MixedWater(chip=output_lines, status=status, action='Off') Valve_PureWater(chip=output_lines, status=status, action='Off') Valve_EnterWater(chip=output_lines, status=status, action='Off') Valve_MainWater(chip=output_lines, status=status, action='Off') client.close() sys.exit(0) if __name__ == "__main__": # Set GPIO output_chip = gpiod.chip('gpiochip11') config = gpiod.line_request() config.consumer = 'output' config.request_type = gpiod.line_request.DIRECTION_OUTPUT output_lines = output_chip.get_lines([0, 1, 2, 3, 4, 5, 6, 7]) output_lines.request(config, default_vals=[0, 0, 0, 0, 0, 0, 0, 0]) status = [0, 0, 0, 0, 0, 0, 0, 0] # When forced to terminate signal.signal(signal.SIGINT, exit_handler) # Set the logger logger = logging.getLogger('sampyo_dio') logger.setLevel(logging.DEBUG) handler = RotatingFileHandler('/home/root/working.log', maxBytes=1024*1024, backupCount=3) handler.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') formatter.converter = seoul_time handler.setFormatter(formatter) logger.addHandler(handler) # Initialized valves main_valve_status = 0 pure_valve_status = 0 Valve_MainWater(chip=output_lines, status=status, action='Off') Valve_PureWater(chip=output_lines, status=status, action='Off') # Read config file with open('./config.json', encoding='UTF-8') as f: jsonData = json.load(f) # Set the weight of water in the chamber volume_water = 1000.0 / float(jsonData['volume-water']) # Set the IP address and port of NodeQ RS-232 module and activate TCP Client modbus_addr = jsonData['modbus-server']['address'] modbus_port = jsonData['modbus-server']['port'] client = ModbusTcpClient(modbus_addr, modbus_port) # Define the default data format data = { "timestamp": 0, "data":{ "weight": 0, "concentration": 0 } } ## Get ProjectCode and AssetCode ## Execution main funcion operation_thread = threading.Thread(target=runAction, args=()) operation_thread.start() tcp_addr = jsonData['tcp-server']['address'] tcp_port = jsonData['tcp-server']['port'] ## Execution TCP/IP server start_server(addr=tcp_addr, port=tcp_port)