plcSimulator.py 12.3 KB
#!/usr/bin/env python3
"""

MODIFIED:
PM 20190108

"""
import os
import sys
import json
import time
import logging
#import urllib.request

DEBUG_FILE = True
PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))

#(_path = os.path.abspath("..")
#(sys.path.insert(0, _path )
import logconf
logger = logging.getLogger(__name__)

#(EP renamed) from utils.Device import Device
from utils.DeviceSim import DeviceSim
from utils.StatusManager import StatusManager

#(EP renamed) class PLCSimulator(Device, StatusManager):
class PLCSimulator(DeviceSim, StatusManager):
    
    # (EP) This simulator will generate some events ?
    EVENT_GENERATOR=False

    lights = "ON"
    shutters = "CLOSE"
    status =""

    def __init__(self, argv):
        # DeviceSim init
        super().__init__(argv)
        # Listen to its own ip and port (the PLC's)
        self.setDevice("PLC")
        self.loop = self.loop_3
        #PM Load PLC status in colibri-new-fixed-2.json
        colibri_json = open("colibri-new-fixed-2.json")
        colibri_struct = json.load(colibri_json)
        self.status = colibri_struct["statuses"][1]["entities"][0]
        #EP added
        if (len(argv) > 1):
            self.setStatusManager("plcSimulator", argv)
            self.EVENT_GENERATOR = True
        #self.set_status()
        #self.my_state.status = 'init'


    def plcPrint(self, string: str):
        if DEBUG_FILE:
            print("PLCSimulator : " + string)

    def updateStatus(self, i: int):
        for dic in self.sims:
            # ex: dic is like "plcSimulator" : {"device_name":"VantagePro", "value_name":"RainRate", "value":12.0} 
            if (int(dic["time"]) == i):
                print(dic)
                change = dic[self.status_name] # ex: {"device_name":"VantagePro", "value_name":"RainRate", "value":12.0} 
                device_name = change["device_name"] # ex: VantagePro
                devices = self.status["devices"]
                for device in devices:
                    if device["device_name"] == device_name:
                        values = device["device_values"]
                        # ex: for VantagePro
                        # values =
                        # {"name": "OutsideTemp", "value": 12.45, "unit": "Celcius", "comment": ""},
                        # {"name": "InsideTemp", "value": 20.28, "unit": "Celcius", "comment": ""},
                        #...
                        for value in values:
                            if value["name"] == change["value_name"]:
                                # This will indirectly update self.status
                                value["value"] = change["value"]
        return 0


    def handleConnections(self):
        print("number of connexions to handle: "+str(len(self.connections)))
        for conn in self.connections:
            print("current connexion: ")
            print(conn)
            data = self.readBytes(1024, conn)
            answer = "["
            count = False
            #if data != "" and self.isError(data) is False:
            if data != "" and not self.isError(data):
                json_array = json.loads(data)
                command_list = json_array["command"]
                for command in command_list:
                    if command["name"] == "STATUS":
                        answer = self.get_status(count, answer)
                        count = True
                    elif command["name"] == "LIST":
                        if count:
                            answer += ','
                        answer += json.dumps(self.status)
                        count = True
                    elif command["name"] == "SWITCH LINES ON":
                        self.lights = "ON"
                        answer = "[Lights turned on"
                    elif command["name"] == "SWITCH LINES OFF":
                        self.lights = "OFF"
                        answer = "[Lights turned off"
                    elif command["name"] == "OPEN SHUTTERS":
                        self.shutters = "OPEN"
                        answer = "[Shutters opened"
                    elif command["name"] == "CLOSE SHUTTERS":
                        self.shutters = "CLOSE"
                        answer = "[Shutters closed"
                    else:
                        print("command received by PLC simulator : [>>> "+ str(command["name"]) + " <<<] unknown")
                answer += ']'
                if len(answer) > 0 and answer != "[]":
                    self.sendMessage(answer, conn)
        return (0)

    def loop(self):
        pass

    def loop_3(self):
        #from simulators.plc.guitalens_worker import WorkerState, WorkerUrl
        from plc.guitalens_worker import WorkerState, WorkerUrl
        # load guitalens_worker
        self.my_state = WorkerState()
        self.my_worker = WorkerUrl(self.my_state, self.status)
        self.my_worker.start()
        self.my_state.status = 'init'
        logger.info('Enter loop_3')
        while (self.my_state.worker == 'start'):
            if (self.isConnected()):
                    self.handleConnections()
            time.sleep(0.5)
        logger.info('Exit loop_3')
        self.my_state.worker == 'stop'

    def loop_2(self):
        #from simulators.plc.meteo_worker import WorkerState, WorkerLog
        from plc.meteo_worker import WorkerState, WorkerLog
        # load meteo_worker
        self.my_state = WorkerState()
        self.my_worker = WorkerLog(self.my_state, self.status)
        self.my_worker.start()
        self.my_state.status = 'init'
        while (self.my_state.worker == 'start'):
            if (self.isConnected()):
                    self.handleConnections()
            time.sleep(0.5)
        self.my_state.worker == 'stop'
        return True

    def loop_1(self):
        if self.EVENT_GENERATOR and not self.parse(): return False
        i = 0
        if (self.EVENT_GENERATOR and self.ended==0):
            self.plcPrint("No entry for PLC found in config file : " + self.config_file)
            return False
        while (True):
            print("\n\n\nIteration "+str(i)+" :")
            if self.EVENT_GENERATOR: self.updateStatus(i)
            if (self.isConnected()):
                self.handleConnections()
            i += 1
            if (self.EVENT_GENERATOR and i > self.ended): return (0)
        return True

    def run(self):
        if DEBUG_FILE:
            print("PLC simulator running...")
        #if self.EVENT_GENERATOR and not self.parse(): return False
        self.configSocket()
        return self.loop()
        #return (0)

    # deprecated
    def set_status(self):
        self.status = {
            "name": "STATUS", "from": "Beckhoff", "version_firmware": "20170809", "site": "OSM-Mexico",
            "date": "2017-03-03T13:45:00",
            "device": [{"name": "WXT520", "type": "meteo", "serial_number": "14656423", "valid": "yes",
                        "values": [
                            {"name": "OutsideTemp", "value": 12.12, "unit": "Celcius", "comment": ""},
                            {"name": "OutsideHumidity", "value": 64.1, "unit": "percent", "comment": ""},
                            {"name": "Pressure", "value": 769.2, "unit": "mbar", "comment": "At site elevation"},
                            {"name": "RainRate", "value": 0.0, "unit": "mm/h", "comment": ""},
                            {"name": "WindSpeed", "value": 3.1, "unit": "m/s", "comment": ""},
                            {"name": "WindDir", "value": 202.3, "unit": "deg", "comment": "(N=0, E=90)"},
                            {"name": "DewPoint", "value": 8.3, "unit": "deg", "comment": ""}
                        ]
                        },
                       {"name": "DRD11", "type": "meteo", "serial_number": "RET6789", "valid": "yes",
                        "values": [
                            {"name": "analog", "value": 2.345, "unit": "V", "comment": "3V=Dry <2.5V=Rain"},
                            {"name": "digital", "value": 1, "unit": "bool", "comment": "1=Dry 0=Rain"}
                        ]
                        },
                       {"name": "VantagePro", "type": "meteo", "serial_number": "ERTRY2344324", "valid": "no",
                        "values": [
                            {"name": "OutsideTemp", "value": 12.45, "unit": "Celcius", "comment": ""},
                            {"name": "InsideTemp", "value": 20.28, "unit": "Celcius", "comment": ""},
                            {"name": "OutsideHumidity", "value": 65.3, "unit": "percent", "comment": ""},
                            {"name": "InsideHumidity", "value": 45.6, "unit": "percent", "comment": ""},
                            {"name": "Pressure", "value": 768.7, "unit": "mbar", "comment": "At site elevation"},
                            {"name": "RainRate", "value": 0.0, "unit": "mm/h", "comment": ""},
                            {"name": "WindSpeed", "value": 2.8, "unit": "m/s", "comment": ""},
                            {"name": "WindDir", "value": 207.0, "unit": "deg", "comment": "(N=0, E=90)"},
                            {"name": "WindDirCardinal", "value": "SW", "unit": "string", "comment": ""},
                            {"name": "DewPoint", "value": 8.5, "unit": "deg", "comment": ""}
                        ]
                        },
                       {"name": "MLX90614-1", "type": "meteo", "serial_number": "Unknown", "valid": "yes",
                        "values": [
                            {"name": "SensorTemperature", "value": 23.56, "unit": "Celcius", "comment": ""},
                            {"name": "SkyTemperature", "value": -15.67, "unit": "Celcius",
                             "comment": "Clear<-10 VeryCloudy>4"}
                        ]
                        },
                       {"name": "LAMP_FLAT_CAGIRE", "type": "calib", "serial_number": "REF_3434", "valid": "yes",
                        "values": [
                            {"name": "status", "value": "on", "unit": "string", "comment": "on or off"},
                            {"name": "current", "value": 0.234, "unit": "Ampere", "comment": ""}
                        ]
                        },
                       {"name": "LAMP FLAT CAGIRE", "type": "calib", "serial_number": "REF_3434", "valid": "yes",
                        "values": [
                            {"name": "status", "value": "on", "unit": "string", "comment": "on or off"},
                            {"name": "current", "value": 0.234, "unit": "Ampere", "comment": ""}
                        ]
                        },
                       {"name": "GLOBAL",
                        "values": [
                            {"name": "mode", "value": "AUTO", "unit": "String", "comment": "AUTO OFF MANU"},
                            {"name": "is_safe", "value": True, "unit": "bool", "comment": "True or False"},
                            {"name": "LIGHTS", "value": self.lights, "unit": "String", "comment": "ON or OFF"},
                            {"name": "SHUTTERS", "value": self.shutters, "unit": "String", "comment": "OPEN OR CLOSE"},
                        ]
                        }
                       ],
        }

        '''
                Si on veut rajouter des éléments dans les json il faut penser a ajouter les méthodes correspondantes dans models.py
        '''

    def get_status(self, count, answer):
        """list = {"name": "LIST", "from": "Beckhoff", "version_firmware": "20170809", "site": "OSM-Mexico",
                "date": "2017-03-03T13:42:46",
                "command": [
                    {"name": "LIST", "type": "",
                     "parameters": ""
                     },
                    {"name": "SATUS", "type": "",
                     "parameters": ""
                     },
                    {"name": "LAMP_FLAT_CAGIRE", "type": "calib",
                     "parameters": [
                         {"name": "value", "unit": "string", "comment": "on or off", "default": ""},
                         {"name": "current", "unit": "Ampere", "comment": "", "default": "0.345"}
                     ]
                     }
                ]
                }"""
        if count:
            answer += ','
        answer += json.dumps(self.status)
        return answer

if __name__ == "__main__":
    sim = PLCSimulator(sys.argv)
    sim.run()