Compare commits

..

No commits in common. "main" and "v0.0.7" have entirely different histories.
main ... v0.0.7

16 changed files with 382 additions and 1097 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +1,11 @@
{ {
"appId": "81a695f0-a990-43c8-998f-2ba1bf9c6005", "appId": "81a695f0-a990-43c8-998f-2ba1bf9c6005",
"modbus-server": { "modbus-server": {
"address": "172.17.16.202", "address": "25.7.55.237",
"port": 5020 "port": 5020
}, },
"tcp-server": { "tcp-server": {
"address": "172.17.16.201", "address": "25.7.57.1",
"port": 24 "port": 7007
}, }
"volume-water": 542 }
}

View File

@ -1,12 +0,0 @@
{
"appId": "81a695f0-a990-43c8-998f-2ba1bf9c6005",
"modbus-server": {
"address": "25.7.55.237",
"port": 5020
},
"tcp-server": {
"address": "25.7.57.1",
"port": 7007
},
"volume-water": 542
}

View File

@ -1,49 +1,26 @@
{ {
"device": { "device": {
"enter": { "setzero": {
"action": "Off" "action": "Off"
}, },
"main": { "measure": {
"action": "Off", "action": "On"
"duration": 4 },
}, "mixed": {
"measure": { "action": "Off",
"action": "On" "duration": 15
}, },
"mixed": { "pure": {
"action": "Off" "action": "Off",
}, "duration": 15
"motor": { },
"action": "Off" "vent": {
}, "action": "Off",
"pure": { "duration": 30
"action": "Off", },
"duration": 4 "motor": {
}, "action": "Off"
"setzero": { }
"action": "Off" },
}, "type": "manual"
"vent": {
"action": "Off"
}
},
"maintenance": {
"clean": {
"duration": 20,
"time": 25
}
},
"type": "manual",
"working-time": {
"step0": 5,
"step1": 10,
"step2": 0.5,
"step3": 4,
"step4": 20,
"step5": 20,
"step6": 0.5,
"step7": 15,
"step8": 10,
"step9": 140
}
} }

View File

@ -1,27 +0,0 @@
{
"device": {
"setzero": {
"action": "Off"
},
"measure": {
"action": "On"
},
"mixed": {
"action": "Off",
"duration": 15
},
"pure": {
"action": "Off",
"duration": 15
},
"vent": {
"action": "Off",
"duration": 30
},
"motor": {
"action":
"On"
}
},
"type": "manual"
}

View File

@ -1,11 +1,11 @@
version: bwc/v2 # bwc 버전 정보입니다. version: bwc/v2 # bwc 버전 정보입니다.
spec: spec:
appName: sampyo-dio-app # 앱의 이름입니다. appName: sampyo-dio-app # 앱의 이름입니다.
runFile: main.py # 앱의 실행 파일입니다. runFile: main.py # 앱의 실행 파일입니다.
env: env:
bin: python3 # 앱을 실행할 바이너라 파일 종류입니다.(장비에 따라 다르므로 확인 후 정의해야 합니다.) bin: python3 # 앱을 실행할 바이너라 파일 종류입니다.(장비에 따라 다르므로 확인 후 정의해야 합니다.)
virtualEnv: base # 사용할 가상환경 이름입니다. virtualEnv: base # 사용할 가상환경 이름입니다.
package: requirements.txt # 설치할 Python 패키지 정보 파일입니다.(기본 값은 requirement.txt 입니다.) package: requirements.txt # 설치할 Python 패키지 정보 파일입니다.(기본 값은 requirement.txt 입니다.)
stackbase: stackbase:
tagName: v0.0.51 # Stackbase(gitea)에 릴리즈 태그명 입니다. tagName: v0.0.7 # Stackbase(gitea)에 릴리즈 태그명 입니다.
repoName: sampyo-dio # Stackbase(gitea)에 저장될 저장소 이릅니다. repoName: sampyo-dio # Stackbase(gitea)에 저장될 저장소 이릅니다.

View File

@ -7,5 +7,5 @@ spec:
virtualEnv: base # 사용할 가상환경 이름입니다. virtualEnv: base # 사용할 가상환경 이름입니다.
package: requirements.txt # 설치할 Python 패키지 정보 파일입니다.(기본 값은 requirement.txt 입니다.) package: requirements.txt # 설치할 Python 패키지 정보 파일입니다.(기본 값은 requirement.txt 입니다.)
stackbase: stackbase:
tagName: v0.0.13 # Stackbase(gitea)에 릴리즈 태그명 입니다. tagName: v0.0.5 # Stackbase(gitea)에 릴리즈 태그명 입니다.
repoName: sampyo-dio # Stackbase(gitea)에 저장될 저장소 이릅니다. repoName: sampyo-dio # Stackbase(gitea)에 저장될 저장소 이릅니다.

945
main.py
View File

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

375
test.py
View File

@ -1,375 +0,0 @@
import ssl
import json
import time
import argparse
import sys, signal
import gpiod
from pymodbus.client import ModbusTcpClient
import AWSIoTPythonSDK.MQTTLib as AWSIoTPyMQTT
import asyncio, pytz
from datetime import datetime
import threading, socket
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')
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
data['data']['weight'] = weight
result = (float(weight) * 1.883239171 * 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')
# set zero
# Set_Zero(client=client)
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(19)
Valve_MixedWater(chip=output_lines, status=status, action='Off')
# time.sleep(20)
time.sleep(10)
# 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(40)
Motor(chip=output_lines, status=status, action='Off')
time.sleep(0.5)
Valve_Vent(chip=output_lines, status=status, action='Off')
# input pure water
Valve_PureWater(chip=output_lines, status=status, action='On')
time.sleep(19)
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(40)
Motor(chip=output_lines, status=status, action='Off')
time.sleep(0.5)
Valve_Vent(chip=output_lines, status=status, action='Off')
time.sleep(5)
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)
if cmd['device']['setzero']['action'] == 'On':
Set_Zero(client=client)
def connectMQTT(clientID, projectCode):
CLIENT_ID = clientID
ENDPOINT = "avk03ee629rck-ats.iot.ap-northeast-2.amazonaws.com"
PATH_TO_CERTIFICATE = f"/etc/sdt/cert/{projectCode}-certificate.pem"
PATH_TO_PRIVATE_KEY = f"/etc/sdt/cert/{projectCode}-private.pem"
PATH_TO_AMAZON_ROOT_CA_1 = f"/etc/sdt/cert/AmazonRootCA1.pem"
myAWSIoTMQTTClient = AWSIoTPyMQTT.AWSIoTMQTTClient(CLIENT_ID)
myAWSIoTMQTTClient.configureEndpoint(ENDPOINT, 8883)
myAWSIoTMQTTClient.configureCredentials(PATH_TO_AMAZON_ROOT_CA_1, PATH_TO_PRIVATE_KEY, PATH_TO_CERTIFICATE)
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(2) # Draining: 2 Hz
return myAWSIoTMQTTClient
def publishMsg(mqttClient, topic, msg):
# Make the copip3 nnect() call
# mqttClient.connect()
while True:
try:
mqttClient.connect()
break
except Exception as e:
print(f'Connection Fail: {e}')
continue
msg['timestamp'] = int(time.time() * 1000)
# Publish message to server desired number of times.
# print('Begin Publish')
mqttClient.publish(topic=topic, payload=json.dumps(msg), QoS=1)
while True:
try:
mqttClient.disconnect()
break
except Exception as e:
print(f'Disconnection Fail: {e}')
continue
def runAction(projectCode, assetCode):
# 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
mqttClient1 = connectMQTT("device-app-test1", projectCode)
mqttClient2 = connectMQTT("device-app-test2", projectCode)
mqttClient3 = connectMQTT("device-app-test3", projectCode)
mqttClient4 = connectMQTT("device-app-test4", projectCode)
mqttClient5 = connectMQTT("device-app-test5", projectCode)
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.
with open('./config.json', encoding='UTF-8') as f:
jsonData = json.load(f)
topic = f"sdtcloud/{projectCode}/{assetCode}/app/{jsonData['appId']}/data"
cnt = 0
while True:
start = time.time()
Command_Read()
# publishMsg(mqttlist[cnt], topic, data)
end = time.time()
cnt += 1
if cnt == 5:
cnt = 0
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():
host = "25.7.57.1"
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)
client = ModbusTcpClient('25.7.55.237', 5020)
parser = argparse.ArgumentParser()
parser.add_argument('-app',help='')
args = parser.parse_args()
# ROOT_PATH = f'/usr/local/sdt/app/{args.app}'
data = {
"timestamp": 0,
"data":{
"weight": 0,
"concentration": 0
}
}
## Get ProjectCode and AssetCode
with open(f'/etc/sdt/device.config/config.json', encoding='UTF-8') as f:
codeData = json.load(f)
## Execution main funcion
operation_thread = threading.Thread(target=runAction, args=(codeData["projectcode"], codeData["assetcode"]))
operation_thread.start()
## Execution TCP/IP server
start_server()

View File

View File