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):
c78dc38b   Quentin Durand   Classe abstraite ...
106
107
                ret = self.sock.recv(size).decode()
                raise (Exception("KO: " + ret))
e8e6f017   Jeremy   Reworked devices ...
108
109
110
111
112
113
            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
114
        except Exception as e:
e8e6f017   Jeremy   Reworked devices ...
115
            if (settings.DEBUG):
605b65e5   Jeremy   Update simulators
116
                self.log(self.name, "Socket not readable : " + str(e))
c78dc38b   Quentin Durand   Classe abstraite ...
117
            return (str(e))
e8e6f017   Jeremy   Reworked devices ...
118
119
120
121
        return (ret)

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

    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...
135
    # TODO: maybe read more than 1024 bytes ?????
e8e6f017   Jeremy   Reworked devices ...
136
    def readMessage(self) -> str:
7e40048f   Quentin Durand   Implementation ba...
137
138
139
140
        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 ...
141
142
143
144
145
146
147
148
149
150
151
152

    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...
153
        Log.objects.create(agent=self.name, message=message)
c53a13e0   Jeremy   Updating a lot of...
154
155
        if DEBUG_FILE and settings.DEBUG:
            self.logger.info("From device : " + device_name + " -> " + message)
e8e6f017   Jeremy   Reworked devices ...
156
157
158
159
160
        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...
161
162
            # (EP) It's a serious bug, so raise exception !!!
            #return (1)
c7583f6e   Etienne Pallier   Cleaner isolation...
163
            raise (Exception(self.name +"Controller has no ip or port to connect to"))
e8e6f017   Jeremy   Reworked devices ...
164
        try:
15e15006   Etienne Pallier   Nouvelles modifs ...
165
            # Create a TCP/IP socket
e8e6f017   Jeremy   Reworked devices ...
166
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
15e15006   Etienne Pallier   Nouvelles modifs ...
167
            # Connect the socket to the port on the server
d2dde3d0   Quentin Durand   Adding the abilit...
168
169
170
171
            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 ...
172
173
174
175
176
177
178
179
180
181
            '''
            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 ...
182
        except Exception as e:
d2dde3d0   Quentin Durand   Adding the abilit...
183
            print(e)
c7583f6e   Etienne Pallier   Cleaner isolation...
184
            print("FAILED TO CONNECT TO DEVICE {}".format(self.name))
e8e6f017   Jeremy   Reworked devices ...
185
            if (settings.DEBUG):
15e15006   Etienne Pallier   Nouvelles modifs ...
186
                #TODO: Bugfix: Ce log ne fait rien !
605b65e5   Jeremy   Update simulators
187
                self.log(self.name, "Failed to connect to " + str(self.ip) + ":" + str(self.port) + " -> " + str(e))
1a2dc19f   Etienne Pallier   nombreux bugfixes...
188
189
190
            # (EP) return TRUE??? (for python, 1=true !!!)
            #return (1)
            return False
e8e6f017   Jeremy   Reworked devices ...
191
        self.connected = True
1a2dc19f   Etienne Pallier   nombreux bugfixes...
192
193
        # (EP) return FALSE ??? (for python, 0=false !!!)
        #return (0)
d2dde3d0   Quentin Durand   Adding the abilit...
194
195
196
197
198
199
200
        return True

    def reconnect(self):

        self.getConfig()
        self.getConnection(self.name)
        try:
81026d94   Quentin Durand   Updating reconnec...
201
202
203
204
            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...
205
206
207
208
209
210
            return True
        except Exception as e:
            print(str(e))
            time.sleep(1)
            return False

81026d94   Quentin Durand   Updating reconnec...
211
212
213
214
215
216
217
    '''
        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")