Add python installation script, openrc run script and CIP400 emulation script
This commit is contained in:
parent
b650cc7427
commit
5a16af939e
|
@ -1,3 +1,4 @@
|
||||||
# cip400
|
# cip400
|
||||||
|
|
||||||
Modbus server (slave) that emulates CIP400 controlles
|
Modbus server (slave) that emulates CIP400 controlles
|
||||||
|
Meant to be run on Alpine linux
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
name="cip400"
|
||||||
|
command="/media/floppy/${name}/run"
|
||||||
|
command_background="yes"
|
||||||
|
pidfile="/run/cip400.pid"
|
|
@ -0,0 +1,135 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import argparse
|
||||||
|
from pyModbusTCP.server import ModbusServer, DataBank, DataHandler
|
||||||
|
from time import sleep, time
|
||||||
|
|
||||||
|
FORCED_OFF = 20210
|
||||||
|
RESET = 20001
|
||||||
|
FULL_STATUS = 19000
|
||||||
|
SELF_STATUS = 19005
|
||||||
|
TIME_OF_DAY = 20200
|
||||||
|
PHOTOCELL = 20202
|
||||||
|
DIMMING = 20203
|
||||||
|
|
||||||
|
|
||||||
|
class MaHandler(DataHandler):
|
||||||
|
def __init__(self, data_bank):
|
||||||
|
super().__init__(data_bank=data_bank)
|
||||||
|
data_bank.last_forcedoff_write = 0
|
||||||
|
|
||||||
|
def read_coils(self, address, count, srv_info):
|
||||||
|
ret = super().read_coils(address, count, srv_info)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def read_d_inputs(self, address, count, srv_info):
|
||||||
|
ret = super().read_d_inputs(address, count, srv_info)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def read_h_regs(self, address, count, srv_info):
|
||||||
|
ret = super().read_h_regs(address, count, srv_info)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def read_i_regs(self, address, count, srv_info):
|
||||||
|
ret = super().read_i_regs(address, count, srv_info)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def write_coils(self, address, bits_l, srv_info):
|
||||||
|
ret = super().write_coils(address, bits_l, srv_info)
|
||||||
|
if address == RESET and bits_l[0]:
|
||||||
|
instance.data_bank.set_holding_registers(FORCED_OFF, [0])
|
||||||
|
instance.data_bank.set_input_registers(FULL_STATUS, [4])
|
||||||
|
instance.data_bank.set_input_registers(SELF_STATUS, [4])
|
||||||
|
instance.data_bank.set_input_registers(TIME_OF_DAY, [255])
|
||||||
|
instance.data_bank.set_input_registers(PHOTOCELL, [255])
|
||||||
|
instance.data_bank.set_holding_registers(DIMMING, [0])
|
||||||
|
instance.data_bank.set_coils(RESET, [False])
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def write_h_regs(self, address, words_l, srv_info):
|
||||||
|
ret = super().write_h_regs(address, words_l, srv_info)
|
||||||
|
if address == DIMMING:
|
||||||
|
print(f"write Dimming {address=} -> {words_l[0]}")
|
||||||
|
if words_l[0] in (0, 128):
|
||||||
|
self.data_bank.set_input_registers(DIMMING, [0])
|
||||||
|
elif words_l[0] == 129:
|
||||||
|
self.data_bank.set_input_registers(DIMMING, [1])
|
||||||
|
elif words_l[0] == 130:
|
||||||
|
self.data_bank.set_input_registers(DIMMING, [2])
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
elif address == TIME_OF_DAY:
|
||||||
|
print(f"write ToD {address=} -> {words_l[0]}")
|
||||||
|
if words_l[0] in (0, 128):
|
||||||
|
self.data_bank.set_input_registers(TIME_OF_DAY, [0])
|
||||||
|
elif words_l[0] == 129:
|
||||||
|
self.data_bank.set_input_registers(TIME_OF_DAY, [1])
|
||||||
|
elif words_l[0] == 130:
|
||||||
|
self.data_bank.set_input_registers(TIME_OF_DAY, [2])
|
||||||
|
elif words_l[0] == 131:
|
||||||
|
self.data_bank.set_input_registers(TIME_OF_DAY, [3])
|
||||||
|
elif words_l[0] == 132:
|
||||||
|
self.data_bank.set_input_registers(TIME_OF_DAY, [4])
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
elif address == FORCED_OFF:
|
||||||
|
print(f"write Forced off {address=} -> {words_l[0]}")
|
||||||
|
self.data_bank.last_forcedoff_write = time()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
start_time = time()
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"-H", "--host", type=str, default="localhost", help="Host (default: localhost)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-p", "--port", type=int, default=11800, help="TCP port (default: 11800)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-d",
|
||||||
|
"--daemon",
|
||||||
|
action="store_true",
|
||||||
|
help="Run as daemon with no 15 min timeout",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
instance = ModbusServer(
|
||||||
|
host=args.host,
|
||||||
|
port=args.port,
|
||||||
|
no_block=True,
|
||||||
|
data_hdl=MaHandler(data_bank=DataBank()),
|
||||||
|
)
|
||||||
|
instance.start()
|
||||||
|
while True:
|
||||||
|
if (
|
||||||
|
not args.daemon
|
||||||
|
and time() - start_time > 60 * 15
|
||||||
|
and time() - instance.data_bank.last_forcedoff_write > 60 * 2
|
||||||
|
):
|
||||||
|
print("input timeout. stopping device")
|
||||||
|
break
|
||||||
|
if time() - instance.data_bank.last_forcedoff_write > 1:
|
||||||
|
instance.data_bank.set_holding_registers(FORCED_OFF, [0])
|
||||||
|
instance.data_bank.set_input_registers(FULL_STATUS, [1])
|
||||||
|
instance.data_bank.set_input_registers(SELF_STATUS, [1])
|
||||||
|
instance.data_bank.set_input_registers(TIME_OF_DAY, [3])
|
||||||
|
instance.data_bank.set_input_registers(PHOTOCELL, [3])
|
||||||
|
instance.data_bank.set_holding_registers(DIMMING, [0])
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
instance.stop()
|
||||||
|
exc = sys.exception()
|
||||||
|
traceback.print_tb(exc.__traceback__, limit=3, file=sys.stdout)
|
||||||
|
print(f"An epic failure just has happened! ({ex})")
|
||||||
|
else:
|
||||||
|
instance.stop()
|
||||||
|
print("bye")
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
cd /media/floppy/cip400/
|
||||||
|
|
||||||
|
log_file="/var/log/cip400.txt"
|
||||||
|
process_ids=""
|
||||||
|
|
||||||
|
trap 'terminate' INT TERM
|
||||||
|
|
||||||
|
venv=".venv"
|
||||||
|
|
||||||
|
if [ ! -d "$venv" ]; then
|
||||||
|
echo "No venv dir. Creating it now..."
|
||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install pyModbusTCP
|
||||||
|
deactivate
|
||||||
|
fi
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
terminate() {
|
||||||
|
for pid in $process_ids; do
|
||||||
|
echo "terminating process $pid" >> $log_file
|
||||||
|
kill $pid
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in $(seq 11800 11829); do
|
||||||
|
echo "starting CIP400 at port $i" >> $log_file
|
||||||
|
python3 cip400.py --host '0.0.0.0' --port $i --daemon &
|
||||||
|
process_ids="$process_ids $!"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo $process_ids >> $log_file
|
||||||
|
|
||||||
|
wait
|
||||||
|
echo 'that is it' >> $log_file
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
# cp /media/floppy/cip400/cip400 /etc/init.d/
|
||||||
|
# chmod 744 /etc/init.d/cip400
|
||||||
|
# rc-update add cip400 default
|
||||||
|
# service cip400 status
|
Loading…
Reference in New Issue