diff --git a/Simulator/plc.py b/Simulator/plc.py new file mode 100755 index 0000000..e79389a --- /dev/null +++ b/Simulator/plc.py @@ -0,0 +1,233 @@ +#!/bin/env python3 + +import socket +import time +import random +import argparse + + +mw1 = 0 +mw2 = 0 +t1 = 0 +t2 = 0 +t3 = 0 +t4 = 0 +t5 = 0 +t6 = 0 +t7 = 0 +t8 = 0 +t9 = 0 +pr1 = 0 +pr2 = 0 + +mw1_max = 5000 +mw2_max = 5000 +t_max = 35000 +pr1_max = 20000 +pr2_max = 15000 + +ENTRY_RATE = 20 / 20 +PUMP_RATE = 5 / 20 + +rw = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + + +def are_valves_open(valves, *ns): + mask = 0 + for n in ns: + mask |= 1 << (n - 1) + return (~valves & mask) == mask + + +def send_cmd(cmd: str, data: str) -> None: + msg = f'(A01{cmd}{data}&' + chk = sum(c for c in msg.encode('ascii')) & 0xFF + msg += f'{chk:02X})\r' + time.sleep(len(msg) * 8 / 9600 * 2) + print(f'-> {msg}') + client.send(msg.encode('ascii')) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('socket') + args = parser.parse_args() + + client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + client.connect(args.socket) + + while True: + line = b'' + while b'\r' not in line: + line += client.recv(256) + cmds = line.split(b'\r') + line = cmds[-1] + cmds = [c.decode('ascii') for c in cmds[:-1]] + for cmd in cmds: + print(f'<- {cmd}') + time.sleep(len(cmd) * 8 / 9600 * 2) + if not cmd.startswith('(A01') or not cmd.endswith(')') or len(cmd) < 7: + send_cmd('CE', '02') + continue + c, d = cmd[4:6], cmd[6:-1] + if cmd[-4] == '&': + chk1 = int(cmd[-3:-1], 16) + chk2 = sum(c for c in cmd[:-3].encode('ascii')) & 0xFF + if chk1 != chk2: + send_cmd('CE', '03') + continue + d = d[:-3] + if c == 'TS': + send_cmd('TS', d) + elif c == 'ST': + send_cmd('ST', '0002') + elif c == 'DR': + # FIXME + d = d.split(',') + s = int(d[0][2:]) + l = int(d[1]) + send_cmd('DR', ''.join(f'{r:04X}' for r in rw[s:s+l])) + elif c == 'DW': + # FIXME + d = d.split(',') + s = int(d[0][2:]) + l = int(d[1]) + for i in range(l): + rw[s + i] = int(d[2 + i], 16) + send_cmd('ST', '0002') + else: + send_cmd('CE', '01') + + # tick - valves + for i in range(0, 60): + want_open = rw[i // 16] & (1 << (i % 16)) == 0 + is_open = rw[10 + i // 16] & (1 << (i % 16)) != 0 + if want_open and not is_open: + if random.random() > 0.875: + rw[10 + i // 16] |= 1 << (i % 16) + elif not want_open and is_open: + if random.random() > 0.875: + rw[10 + i // 16] &= ~(1 << (i % 16)) + + # tick - wannen + want_a1_selected = (rw[5] & 0x0004) != 0 + want_a2_selected = not want_a1_selected + is_a1_selected = (rw[14] & 0x0200) != 0 + is_a2_selected = (rw[14] & 0x0100) != 0 + if is_a1_selected and not want_a1_selected: + rw[14] &= 0xFDFF + elif not is_a1_selected and want_a1_selected: + if random.random() > 0.875: + rw[14] |= 0x0200 + if is_a2_selected and not want_a2_selected: + rw[14] &= 0xFEFF + elif not is_a2_selected and want_a2_selected: + if random.random() > 0.875: + rw[14] |= 0x0100 + + # tick - pipeline + valves = (rw[13] << 48) | (rw[12] << 32) | (rw[11] << 16) | rw[10] + wannen_schnecke = (rw[5] & 0x1) != 0 + mp1 = (rw[4] & 0x002) != 0 + mp2 = (rw[4] & 0x004) != 0 + p12 = (rw[4] & 0x010) != 0 + p3 = (rw[4] & 0x020) != 0 + p4 = (rw[4] & 0x040) != 0 + p5 = (rw[4] & 0x080) != 0 + p6 = (rw[4] & 0x100) != 0 + if wannen_schnecke and is_a1_selected: + mw1 += ENTRY_RATE + elif wannen_schnecke and is_a2_selected: + mw2 += ENTRY_RATE + + if mp1 and mw1 > 0: + mw1 -= PUMP_RATE + if are_valves_open(valves, 1, 6, 51) or are_valves_open(valves, 4, 8, 52): + pr1 += PUMP_RATE + elif are_valves_open(valves, 1, 6, 55) or are_valves_open(valves, 4, 8, 56): + pr2 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 22) or are_valves_open(valves, 4, 7, 9, 13, 23): + t1 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 24) or are_valves_open(valves, 4, 7, 9, 13, 25): + t2 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 26) or are_valves_open(valves, 4, 7, 9, 13, 27): + t3 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 28, 30) or are_valves_open(valves, 4, 7, 9, 13, 29, 31): + t4 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 28, 32) or are_valves_open(valves, 4, 7, 9, 13, 29, 33): + t5 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 28, 34, 37) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 36): + t6 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 28, 34, 39) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 38): + t7 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 28, 34, 41) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 40): + t8 += PUMP_RATE + elif are_valves_open(valves, 1, 5, 12, 28, 34, 43) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 42): + t9 += PUMP_RATE + + if mp2 and mw2 > 0: + mw2 -= PUMP_RATE + if are_valves_open(valves, 2, 8, 52) or are_valves_open(valves, 3, 6, 51): + pr1 += PUMP_RATE + elif are_valves_open(valves, 2, 8, 56) or are_valves_open(valves, 3, 6, 55): + pr2 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 22) or are_valves_open(valves, 2, 7, 9, 13, 23): + t1 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 24) or are_valves_open(valves, 2, 7, 9, 13, 25): + t2 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 26) or are_valves_open(valves, 2, 7, 9, 13, 27): + t3 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 28, 30) or are_valves_open(valves, 2, 7, 9, 13, 29, 31): + t4 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 28, 32) or are_valves_open(valves, 2, 7, 9, 13, 29, 33): + t5 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 28, 34, 37) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 36): + t6 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 28, 34, 39) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 38): + t7 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 28, 34, 41) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 40): + t8 += PUMP_RATE + elif are_valves_open(valves, 3, 5, 12, 28, 34, 43) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 42): + t9 += PUMP_RATE + + if p6 and (t6 > 0 or t7 > 0 or t8 > 0 or t9 > 0): + if t6 > 0: + t6 -= PUMP_RATE + if t7 > 0: + t7 -= PUMP_RATE + if t8 > 0: + t8 -= PUMP_RATE + if t9 > 0: + t9 -= PUMP_RATE + if are_valves_open(valves, 53): + pr1 += PUMP_RATE + elif are_valves_open(valves, 57): + pr2 += PUMP_RATE + + # tick - fill levels + rw[14] &= 0xFF00 + rw[14] |= 0x0004 if mw1 > 50 else 0 + rw[14] |= 0x0002 if mw1 > mw1_max / 2 else 0 + rw[14] |= 0x0001 if mw1 > mw1_max * 0.875 else 0 + rw[14] |= 0x0020 if mw2 > 50 else 0 + rw[14] |= 0x0010 if mw2 > mw2_max / 2 else 0 + rw[14] |= 0x0008 if mw2 > mw2_max * 0.875 else 0 + rw[14] |= 0x0040 if pr1 > pr1_max * 0.875 else 0 + rw[14] |= 0x0080 if pr2 > pr2_max * 0.875 else 0 + rw[16] &= 0xC000 + rw[16] |= 0x0001 if t1 > 50 else 0 + rw[16] |= 0x0002 if t1 > t_max * 0.875 else 0 + rw[16] |= 0x0004 if t2 > 50 else 0 + rw[16] |= 0x0008 if t2 > t_max * 0.875 else 0 + rw[16] |= 0x0010 if t3 > 50 else 0 + rw[16] |= 0x0020 if t3 > t_max * 0.875 else 0 + rw[16] |= 0x0040 if t4 > 50 else 0 + rw[16] |= 0x0080 if t4 > t_max * 0.875 else 0 + rw[16] |= 0x0100 if t5 > 50 else 0 + rw[16] |= 0x0200 if t5 > t_max * 0.875 else 0 + rw[16] |= 0x0400 if t6 > t_max * 0.875 else 0 + rw[16] |= 0x0800 if t7 > t_max * 0.875 else 0 + rw[16] |= 0x1000 if t8 > t_max * 0.875 else 0 + rw[16] |= 0x2000 if t9 > t_max * 0.875 else 0 + + print(mw1, mw2, t1, t2, t3, t4, t5, t6, t7, t8, t9, pr1, pr2)