Blame view

src/devices/Device.py 7.63 KB
e8e6f017   Jeremy   Reworked devices ...
1
from django.conf import settings
ac26ad2b   haribo   Date: 22/07/2016
2
import socket
bb0fbab1   haribo   Fini de mettre la...
3
import configparser
e8e6f017   Jeremy   Reworked devices ...
4
5
import abc
import select
bca9a283   Jeremy   Reworked the sche...
6
import utils.Logger as L
d2dde3d0   Quentin Durand   Adding the abilit...
7
8
import time
import os
76dfa189   Unknown   Adding devices lo...
9
from common.models import Log
bb0fbab1   haribo   Fini de mettre la...
10

0b83e9b3   Quentin Durand   Activate Device a...
11
DEBUG_FILE = True
81026d94   Quentin Durand   Updating reconnec...
12
RECONNECT_TIMEOUT_SECONDS = 20
257abe9b   Jeremy   Added comments
13

e8e6f017   Jeremy   Reworked devices ...
14
'''
257abe9b   Jeremy   Added comments
15
    Generic object for the communication with all the devices
e8e6f017   Jeremy   Reworked devices ...
16
'''
9774228b   haribo   Date: 22/06/2016
17

65149de7   Jeremy   Update
18

ddf59dd4   haribo   Remaniement :
19
class DeviceController():
e8e6f017   Jeremy   Reworked devices ...
20
21
22
23
24
25
26
27
28
    __metaclass__ = abc.ABCMeta
    logger = L.setupLogger("DeviceLogger", "Devices")
    name = ""
    config_file = "../config/socket_config.ini"
    config = None
    connected = False
    sock = None
    ip = None
    port = None
9b5bad52   haribo   Commented all the...
29

bb0fbab1   haribo   Fini de mettre la...
30
    def __init__(self, device_name):
e8e6f017   Jeremy   Reworked devices ...
31
32
33
34
35
        self.name = device_name
        self.getConfig()
        self.getConnection(device_name)
        self.connect()

d2dde3d0   Quentin Durand   Adding the abilit...
36

e8e6f017   Jeremy   Reworked devices ...
37
38
39
40
41
42
43
44
45
46
47
48
    @abc.abstractmethod
    def set(self, command: str, *args):
        return

    @abc.abstractmethod
    def do(self, command: str, *args):
        return

    @abc.abstractmethod
    def get(self, command: str, *args):
        return

ce470283   Jeremy   Plc simulator fin...
49
50
51
52
    @abc.abstractmethod
    def getStatus(self):
        return

d2dde3d0   Quentin Durand   Adding the abilit...
53
    def sendMessage(self, message: str):
e8e6f017   Jeremy   Reworked devices ...
54
        if (not self.isConnected()):
1a2dc19f   Etienne Pallier   nombreux bugfixes...
55
56
57
            # (EP) NON, return 0 !!!
            #return (1)
            return False
c7583f6e   Etienne Pallier   Cleaner isolation...
58
        # (EP) Here we are still not sure that we are connected to the device (it may be down and we do not know)
e8e6f017   Jeremy   Reworked devices ...
59
        try:
f7dd3df1   Jeremy   Update simulators...
60
            readable, writable, exceptional = select.select([], [self.sock], [self.sock], 0)
e8e6f017   Jeremy   Reworked devices ...
61
62
63
            if not (writable or exceptional):
                raise (Exception("Socket not writable"))
            self.sock.send(bytes(message, "UTF-8"))
605b65e5   Jeremy   Update simulators
64
        except Exception as e:
e8e6f017   Jeremy   Reworked devices ...
65
            if (settings.DEBUG):
c7583f6e   Etienne Pallier   Cleaner isolation...
66
67
                print("(ENV) Could not send message (on socket) to "+self.name+" : " + message + " -> " + str(e))
                #EP TODO: bugfix: ce log ne fait rien du tout !!
605b65e5   Jeremy   Update simulators
68
                self.log(self.name, "Could not send message on socket : " + message + " -> " + str(e))
d2dde3d0   Quentin Durand   Adding the abilit...
69

81026d94   Quentin Durand   Updating reconnec...
70
71
72
73
74
                i = 0
                while self.reconnect() is False and i < RECONNECT_TIMEOUT_SECONDS:
                    i += 1
                if i == RECONNECT_TIMEOUT_SECONDS:
                    self.device_is_dead()
15e15006   Etienne Pallier   Nouvelles modifs ...
75
76
77
78
79
            # (EP) NON, 1 = true !!!
            #return (1)
            return False
        # (EP) NON, 0 = false !!!
        #return (0)
7e40048f   Quentin Durand   Implementation ba...
80
        Log.objects.create(agent=self.name, message='Message sent : ' + message)
15e15006   Etienne Pallier   Nouvelles modifs ...
81
        return True
e8e6f017   Jeremy   Reworked devices ...
82
83

    def isError(self, message: str):
15e15006   Etienne Pallier   Nouvelles modifs ...
84
85
86
        #EP
        #if (message == "FAILED" or message == "NOT_SET" or message == "DISCONNECTED"):
        if (message == "FAILED" or message.startswith("NOT_SET") or message == "DISCONNECTED"):
e8e6f017   Jeremy   Reworked devices ...
87
88
89
            return (True)
        return (False)

c7583f6e   Etienne Pallier   Cleaner isolation...
90
    # MIND: Does not mean "is connected now" but "WAS connected once"
e8e6f017   Jeremy   Reworked devices ...
91
92
    def isConnected(self):
        if (not self.sock or not self.connected):
1a2dc19f   Etienne Pallier   nombreux bugfixes...
93
94
            # (EP) NON, c'est totalement illisible !!! on n'est plus en 1970
            #if (self.connect()): return (False)
c7583f6e   Etienne Pallier   Cleaner isolation...
95
            print("something is wrong with this device, now trying to connect again...")
15e15006   Etienne Pallier   Nouvelles modifs ...
96
            if not self.connect(): return False
c7583f6e   Etienne Pallier   Cleaner isolation...
97
            #self.connected = True
15e15006   Etienne Pallier   Nouvelles modifs ...
98
        return True
e8e6f017   Jeremy   Reworked devices ...
99
100
101

    def readBytes(self, size) -> str:
        if (not self.isConnected()):
15e15006   Etienne Pallier   Nouvelles modifs ...
102
            return ("NOT_SET2")
65149de7   Jeremy   Update
103
        try:
f7dd3df1   Jeremy   Update simulators...
104
            readable, writable, exceptional = select.select([self.sock], [], [self.sock], 0)
e8e6f017   Jeremy   Reworked devices ...
105
            if not (readable or exceptional):
f47ec727   Quentin Durand   hotfix monitoring
106
107
108
                #ret = self.sock.recv(size).decode()
                raise (Exception("KO: socket error"))

e8e6f017   Jeremy   Reworked devices ...
109
110
111
112
113
114
            ret = self.sock.recv(size).decode()
            if (not ret):
                if (settings.DEBUG):
                    self.log(self.name, "Connection has been killed")
                self.connected = False
                return ("DISCONNECTED")
605b65e5   Jeremy   Update simulators
115
        except Exception as e:
e8e6f017   Jeremy   Reworked devices ...
116
            if (settings.DEBUG):
605b65e5   Jeremy   Update simulators
117
                self.log(self.name, "Socket not readable : " + str(e))
c78dc38b   Quentin Durand   Classe abstraite ...
118
            return (str(e))
e8e6f017   Jeremy   Reworked devices ...
119
120
121
122
        return (ret)

    def blockAndReadBytes(self, size) -> str:
        self.sock.setblocking(1)
7e40048f   Quentin Durand   Implementation ba...
123
124
125
        message = self.readBytes(size)
        Log.objects.create(agent=self.name, message='Message received : ' + message)
        return (message)
e8e6f017   Jeremy   Reworked devices ...
126
127
128
129
130
131
132
133
134
135

    def blockAndReadMessage(self) -> str:
        return (self.blockAndReadBytes(1024))

    def setBlocking(self, flag):
        if (not self.sock):
            return (1)
        self.sock.setblocking(flag)
        return (0)

d3faf327   Quentin Durand   Command control D...
136
    # TODO: maybe read more than 1024 bytes ?????
e8e6f017   Jeremy   Reworked devices ...
137
    def readMessage(self) -> str:
7e40048f   Quentin Durand   Implementation ba...
138
139
140
141
        message = str(self.readBytes(1024))
        #os.system("echo lol >> /home/portos/IRAP/pyros/src/POURKOICAMARCHPA")
        Log.objects.create(agent=self.name, message='Message received : ' + message)
        return message
e8e6f017   Jeremy   Reworked devices ...
142
143
144
145
146
147
148
149
150
151
152
153

    def getConnection(self, device_name):
        self.ip = self.config.get(device_name, "ip")
        self.port = int(self.config.get(device_name, "port"))
        return (0)

    def getConfig(self):
        self.config = configparser.ConfigParser()
        self.config.read(self.config_file)
        return (0)

    def log(self, device_name: str, message: str):
76dfa189   Unknown   Adding devices lo...
154
        Log.objects.create(agent=self.name, message=message)
c53a13e0   Jeremy   Updating a lot of...
155
156
        if DEBUG_FILE and settings.DEBUG:
            self.logger.info("From device : " + device_name + " -> " + message)
e8e6f017   Jeremy   Reworked devices ...
157
158
159
160
161
        return (0)

    def connect(self):
        if (self.ip is None or self.port is None):
            self.log(self.name, "Ip or Port not initialized")
1a2dc19f   Etienne Pallier   nombreux bugfixes...
162
163
            # (EP) It's a serious bug, so raise exception !!!
            #return (1)
c7583f6e   Etienne Pallier   Cleaner isolation...
164
            raise (Exception(self.name +"Controller has no ip or port to connect to"))
e8e6f017   Jeremy   Reworked devices ...
165
        try:
15e15006   Etienne Pallier   Nouvelles modifs ...
166
            # Create a TCP/IP socket
e8e6f017   Jeremy   Reworked devices ...
167
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
15e15006   Etienne Pallier   Nouvelles modifs ...
168
            # Connect the socket to the port on the server
d2dde3d0   Quentin Durand   Adding the abilit...
169
170
171
172
            r = self.sock.connect_ex((self.ip, self.port))
            if (r != 0):
                raise Exception("sock.connect_ex failed : " + os.strerror(r))

15e15006   Etienne Pallier   Nouvelles modifs ...
173
174
175
176
177
178
179
180
181
182
            '''
            Socket set to non blocking (by default, it is blocking)
            In non-blocking mode, if a recv() call doesn’t find any data, 
            or if a send() call can’t immediately dispose of the data, 
            an error exception is raised. 
            (In blocking mode, the calls block until they can proceed) 
            s.setblocking(0) is equivalent to s.settimeout(0.0); 
            s.setblocking(1) is equivalent to s.settimeout(None).
            '''
            self.sock.setblocking(0)
e8e6f017   Jeremy   Reworked devices ...
183
        except Exception as e:
d2dde3d0   Quentin Durand   Adding the abilit...
184
            print(e)
c7583f6e   Etienne Pallier   Cleaner isolation...
185
            print("FAILED TO CONNECT TO DEVICE {}".format(self.name))
e8e6f017   Jeremy   Reworked devices ...
186
            if (settings.DEBUG):
15e15006   Etienne Pallier   Nouvelles modifs ...
187
                #TODO: Bugfix: Ce log ne fait rien !
605b65e5   Jeremy   Update simulators
188
                self.log(self.name, "Failed to connect to " + str(self.ip) + ":" + str(self.port) + " -> " + str(e))
1a2dc19f   Etienne Pallier   nombreux bugfixes...
189
190
191
            # (EP) return TRUE??? (for python, 1=true !!!)
            #return (1)
            return False
e8e6f017   Jeremy   Reworked devices ...
192
        self.connected = True
1a2dc19f   Etienne Pallier   nombreux bugfixes...
193
194
        # (EP) return FALSE ??? (for python, 0=false !!!)
        #return (0)
d2dde3d0   Quentin Durand   Adding the abilit...
195
196
197
198
199
200
201
        return True

    def reconnect(self):

        self.getConfig()
        self.getConnection(self.name)
        try:
81026d94   Quentin Durand   Updating reconnec...
202
203
204
205
            print("\nTrying to reconnect to " + str(self.name) + "...")
            self.log(self.name, "Trying to reconnect to " + self.name)
            if not self.connect():
                raise Exception("Reconnect to " + self.name + " failed")
d2dde3d0   Quentin Durand   Adding the abilit...
206
207
208
209
210
211
            return True
        except Exception as e:
            print(str(e))
            time.sleep(1)
            return False

81026d94   Quentin Durand   Updating reconnec...
212
213
214
215
216
217
218
    '''
        Function called when the connection with the device is lost for too lon time and the max timeout for reconnection
        RECONNECT_TIMEOUT_SECONDS is reached 
    '''

    def device_is_dead(self):
        print("\nCan't establish connection with the " + self.name + ", timeout expired")