diff --git a/config.json b/config.json index 4bf82ef..7a0e176 100644 --- a/config.json +++ b/config.json @@ -1,12 +1,12 @@ -{ - "appId": "81a695f0-a990-43c8-998f-2ba1bf9c6005", - "modbus-server": { - "address": "172.17.16.202", - "port": 5020 - }, - "tcp-server": { - "address": "172.17.16.201", - "port": 24 - }, - "volume-water": 542 -} +{ + "appId": "81a695f0-a990-43c8-998f-2ba1bf9c6005", + "modbus-server": { + "address": "172.17.16.202", + "port": 5020 + }, + "tcp-server": { + "address": "172.17.16.201", + "port": 24 + }, + "volume-water": 542 +} diff --git a/control.json b/control.json index c388abc..826aaf6 100644 --- a/control.json +++ b/control.json @@ -1,27 +1,29 @@ -{ - "device": { - "setzero": { - "action": "Off" - }, - "measure": { - "action": "On", - "duration": 20 - }, - "mixed": { - "action": "Off", - "duration": 15 - }, - "pure": { - "action": "Off", - "duration": 15 - }, - "vent": { - "action": "Off", - "duration": 30 - }, - "motor": { - "action": "Off" - } - }, - "type": "manual" -} +{ + "device": { + "setzero": { + "action": "Off" + }, + "measure": { + "action": "On", + "duration": 20 + }, + "mixed": { + "action": "Off", + "duration": 7 + }, + "pure": { + "action": "Off", + "duration": 10, + "holding": 5 + }, + "vent": { + "action": "Off", + "duration": 30, + "holding": 2 + }, + "motor": { + "action": "Off" + } + }, + "type": "manual" +} diff --git a/framework.yaml b/framework.yaml index b14d646..6777616 100644 --- a/framework.yaml +++ b/framework.yaml @@ -7,5 +7,5 @@ spec: virtualEnv: base # 사용할 가상환경 이름입니다. package: requirements.txt # 설치할 Python 패키지 정보 파일입니다.(기본 값은 requirement.txt 입니다.) stackbase: - tagName: v0.0.14 # Stackbase(gitea)에 릴리즈 태그명 입니다. + tagName: v0.0.15 # Stackbase(gitea)에 릴리즈 태그명 입니다. repoName: sampyo-dio # Stackbase(gitea)에 저장될 저장소 이릅니다. diff --git a/main.py b/main.py index da5aac7..4568d49 100644 --- a/main.py +++ b/main.py @@ -1,344 +1,370 @@ -import json -import time -import argparse -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 - -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): - if action == 'On': - status[3] = 1 - else: # action == 'Off' - status[3] = 0 - - chip.set_values(status) - -def Measure_Weight(client): - # print('In') - val = 0 - try: - result = client.read_holding_registers(1, 1) - if result.isError(): - print(f'Error: {result}') - else: - val = result.registers[0] - val -= 1000 - val /= 1000 - print(f'value: {val}') - except Exception as e: - pass - - return 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}') - -def Set_Zero(client): - client.write_coil(1, 1) - -def Command_Read(): - with open('./control.json', 'r') as f: - cmd = json.load(f) - - if cmd['type'] == 'auto': - - Valve_Vent(chip=output_lines, status=status, action='Off') - Motor(chip=output_lines, status=status, action='Off') - - mixed_duration = int(cmd['device']['mixed']['duration']) - pure_duration = int(cmd['device']['pure']['duration']) - vent_duration = int(cmd['device']['vent']['duration']) - measure_duration = int(cmd['device']['vent']['duration']) - - time.sleep(5) - start = Measure_Weight(client=client) - time.sleep(5) - - # input mixed water - Valve_MixedWater(chip=output_lines, status=status, action='On') - time.sleep(mixed_duration) - Valve_MixedWater(chip=output_lines, status=status, action='Off') - time.sleep(measure_duration) - - # measure weight - end = Measure_Weight(client=client) - time.sleep(1) - - Calculate_Concentration(weight=(float(end)-float(start))) - - # vent mixed water - Valve_Vent(chip=output_lines, status=status, action='On') - time.sleep(0.5) - Motor(chip=output_lines, status=status, action='On') - time.sleep(vent_duration) - Motor(chip=output_lines, status=status, action='Off') - time.sleep(0.5) - Valve_Vent(chip=output_lines, status=status, action='Off') - time.sleep(0.5) - - # input pure water - Valve_PureWater(chip=output_lines, status=status, action='On') - time.sleep(pure_duration) - Valve_PureWater(chip=output_lines, status=status, action='Off') - time.sleep(0.5) - - # vent pure water - Valve_Vent(chip=output_lines, status=status, action='On') - time.sleep(0.5) - Motor(chip=output_lines, status=status, action='On') - time.sleep(vent_duration) - Motor(chip=output_lines, status=status, action='Off') - time.sleep(0.5) - Valve_Vent(chip=output_lines, status=status, action='Off') - time.sleep(1) - - return 1 - - 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']) - 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 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 - 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 - 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() - - diff = end - start - if diff < 3: - time.sleep(3 - diff) - -def handle_client(conn, ip, port): - global data - 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) - - 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 - 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] == '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 # "25.7.57.1" - 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 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') - - client.close() - - sys.exit(0) - -if __name__ == "__main__": - 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] - - signal.signal(signal.SIGINT, exit_handler) - - with open('./config.json', encoding='UTF-8') as f: - jsonData = json.load(f) - - volume_water = 1000.0 / float(jsonData['volume-water']) - - modbus_addr = jsonData['modbus-server']['address'] - modbus_port = jsonData['modbus-server']['port'] - - client = ModbusTcpClient(modbus_addr, modbus_port) - - 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) +import json +import time +import argparse +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 + +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): + if action == 'On': + status[3] = 1 + else: # action == 'Off' + status[3] = 0 + + chip.set_values(status) + +def Measure_Weight(client): + # print('In') + val = 0 + try: + result = client.read_holding_registers(1, 1) + if result.isError(): + print(f'Error: {result}') + else: + val = result.registers[0] + val -= 1000 + val /= 1000 + print(f'value: {val}') + except Exception as e: + pass + + return 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}') + +def Set_Zero(client): + client.write_coil(1, 1) + +def Command_Read(): + with open('./control.json', 'r') as f: + cmd = json.load(f) + + if cmd['type'] == 'auto': + + Valve_Vent(chip=output_lines, status=status, action='Off') + Motor(chip=output_lines, status=status, action='Off') + + mixed_duration = int(cmd['device']['mixed']['duration']) + pure_duration = int(cmd['device']['pure']['duration']) + pure_holding = int(cmd['device']['pure']['holding']) + vent_duration = int(cmd['device']['vent']['duration']) + vent_holding = int(cmd['device']['vent']['holding']) + measure_duration = int(cmd['device']['measure']['duration']) + + time.sleep(7) + start = Measure_Weight(client=client) + time.sleep(5) + + # input mixed water + Valve_MixedWater(chip=output_lines, status=status, action='On') + time.sleep(mixed_duration) + Valve_MixedWater(chip=output_lines, status=status, action='Off') + time.sleep(measure_duration) + + # measure weight + end = Measure_Weight(client=client) + time.sleep(1) + + Calculate_Concentration(weight=(float(end)-float(start))) + + # # vent mixed water + # Valve_Vent(chip=output_lines, status=status, action='On') + # time.sleep(0.5) + # Motor(chip=output_lines, status=status, action='On') + # time.sleep(vent_duration) + # Motor(chip=output_lines, status=status, action='Off') + # time.sleep(0.5) + # Valve_Vent(chip=output_lines, status=status, action='Off') + # time.sleep(0.5) + + # # input pure water + # Valve_PureWater(chip=output_lines, status=status, action='On') + # time.sleep(pure_duration) + # Valve_PureWater(chip=output_lines, status=status, action='Off') + # time.sleep(0.5) + + # # vent pure water + # Valve_Vent(chip=output_lines, status=status, action='On') + # time.sleep(0.5) + # Motor(chip=output_lines, status=status, action='On') + # time.sleep(vent_duration) + # Motor(chip=output_lines, status=status, action='Off') + # time.sleep(0.5) + # Valve_Vent(chip=output_lines, status=status, action='Off') + # time.sleep(1) + + # Vent and Clear sequence + # 1) Open Vent and wait for 2 sec + Valve_Vent(chip=output_lines, status=status, action='On') + time.sleep(vent_holding) + + # 2) Motor On + Motor(chip=output_lines, status=status, action='On') + time.sleep(0.5) + + # 3) Input Pure Water + Valve_PureWater(chip=output_lines, status=status, action='On') + time.sleep(pure_duration + pure_holding) + Valve_PureWater(chip=output_lines, status=status, action='Off') + time.sleep(0.5) + + # 4) Wait until empty + time.sleep(vent_duration) + + # 5) Motor Off and Vent close + Motor(chip=output_lines, status=status, action='Off') + time.sleep(0.5) + Valve_Vent(chip=output_lines, status=status, action='Off') + time.sleep(0.5) + + return 1 + + 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']) + 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 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 + 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 + 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() + + diff = end - start + if diff < 3: + time.sleep(3 - diff) + +def handle_client(conn, ip, port): + global data + 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) + + 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 + 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] == '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 # "25.7.57.1" + 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 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') + + client.close() + + sys.exit(0) + +if __name__ == "__main__": + 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] + + signal.signal(signal.SIGINT, exit_handler) + + with open('./config.json', encoding='UTF-8') as f: + jsonData = json.load(f) + + volume_water = 1000.0 / float(jsonData['volume-water']) + + modbus_addr = jsonData['modbus-server']['address'] + modbus_port = jsonData['modbus-server']['port'] + + client = ModbusTcpClient(modbus_addr, modbus_port) + + 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)