-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
253 lines (216 loc) · 9.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
import os
import socket
import sys
import time
import traceback
import threading
from collections import deque
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QWidget, QApplication
from hmi import *
########## Global variables
# Create the main queue object, if can't then exit the app.
MAX_QUEUE_SIZE = 100
try:
MAIN_QUEUE = deque(maxlen=MAX_QUEUE_SIZE)
except:
print('---It failed to create the main queue object! System exits!---')
sys.exit(traceback.format_exc())
else:
STATE = 'INITIALIZE'
DATA = None
MAIN_QUEUE.append({'STATE':STATE, 'DATA':DATA})
finally:
pass
# Controller connected
CONTROLLER_CONNECTED = False
# Global exit
GLOBAL_EXIT = False
# Global message handling loop error
GLOBAL_MHL_ERROR_DATA = None
GLOBAL_MHL_ERROR_ERROR = False
GLOBAL_MHL_ERROR = {'ERROR':GLOBAL_MHL_ERROR_ERROR, 'DATA':GLOBAL_MHL_ERROR_DATA}
# Global GUI-event loop error
GLOBAL_EHL_ERROR_DATA = None
GLOBAL_EHL_ERROR_ERROR = False
GLOBAL_EHL_ERROR = {'ERROR':GLOBAL_EHL_ERROR_ERROR, 'DATA':GLOBAL_EHL_ERROR_DATA}
# Maximum UDP bytes
MAX_BYTES = 65535
########## Functions
pass
########## Classes
# PyQt5 application window
class AppWindow(QWidget):
def __init__(self):
super().__init__()
self.ui = Ui_hmi()
self.ui.setupUi(self)
# Setting the fixed size of window
width = 800
height = 400
self.setFixedSize(width, height)
# Define callback functions
self.ui.Exit_Btn.clicked.connect(self.exit_btn)
self.ui.Connect_Controller_Btn.clicked.connect(self.connect_controller_btn)
self.ui.Run_Control_Btn.clicked.connect(self.run_control_btn)
self.ui.Stop_Control_Btn.clicked.connect(self.stop_control_btn)
self.ui.Update_Control_Config_Btn.clicked.connect(self.update_control_config_btn)
self.ui.Update_Setpoint_Edit.returnPressed.connect(self.update_setpoint_edit)
self.timer_for_timeout = QTimer()
self.timer_for_timeout.timeout.connect(self.timer_for_timeout_fcn)
self.timer_for_timeout.start(100) # add a timeout to the event loop!
self.show()
return None
def exit_btn(self):
# print(MAIN_QUEUE)
self.close() # This will call the function self.closeEvent
return None
def connect_controller_btn(self):
STATE = 'CONNECT CONTROLLER'
DATA = None
MAIN_QUEUE.append({'STATE':STATE, 'DATA':DATA}) # MAIN_QUEUE.append(...) doesn't generate exceptions.
# When appending, if the queue is full, an exisiting
# element will be removed!
# print(MAIN_QUEUE)
return None
def stop_control_btn(self):
STATE = 'STOP CONTROL'
DATA = None
MAIN_QUEUE.append({'STATE':STATE, 'DATA':DATA})
# print(MAIN_QUEUE)
return None
def run_control_btn(self):
STATE = 'RUN CONTROL'
DATA = None
MAIN_QUEUE.append({'STATE':STATE, 'DATA':DATA})
# print(MAIN_QUEUE)
return None
def update_control_config_btn(self):
STATE = 'UPDATE CONTROL CONFIG'
DATA = {'Kp':1.0, 'Ki':1.0, 'Kd':1.0, 'High':1.0, 'Low':0.0}
MAIN_QUEUE.append({'STATE':STATE, 'DATA':DATA})
# print(MAIN_QUEUE)
return None
def update_setpoint_edit(self):
STATE = 'UPDATE SETPOINT'
try:
setpoint = float(self.ui.Update_Setpoint_Edit.text())
except:
pass
else:
DATA = {'Setpoint':setpoint}
MAIN_QUEUE.append({'STATE':STATE, 'DATA':DATA})
finally:
pass
# print(MAIN_QUEUE)
return None
def closeEvent(self, event):
print('---Cleaning, flushing queue, closing ports, etc.---')
# Close network socket
STATE = 'EXIT'
DATA = None
MAIN_QUEUE.append({'STATE':STATE, 'DATA':DATA}) # Add 'EXIT' state so that the MHL will terminate.
event.accept() # or event.ignore()
print('---Application is closed!---')
return None
def timer_for_timeout_fcn(self):
# Chech global GLOBAL_EXIT.
if GLOBAL_EXIT:
MAIN_QUEUE.appendleft({'STATE':'EXIT', 'DATA':None})
self.close()
return None
# Thread for the message handling loop
class MessageHandlingLoop(threading.Thread):
def __init__(self, app_window):
threading.Thread.__init__(self)
self.app_window = app_window
return None
def run(self):
print('---The message handling loop starts---')
while True:
# Dequeue elements: create a function names dequeue_manager(),
# input arg/s : MAIN_QUEUE
# output/s : state_name and state_data
# The function should handle exceptions and return error.
# Implement an early function-return with True at the end of try-clause, if no exceptions occured.
# Implement an early function-return with False in finally section, if exceptions occured.
try:
dequed_data = MAIN_QUEUE.popleft()
except:
state_name = ''
state_data = None
else: # Execute if the try clause does not raise an exception
state_name = dequed_data['STATE']
state_data = dequed_data['DATA']
print(state_name)
finally:
pass
# Implement the state machine: create a function for the state machine (?).
if state_name == 'INITIALIZE':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
pass
elif state_name == 'EXIT':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
MAIN_QUEUE.clear()
print('---The event handler ends---')
return None
elif state_name == 'CONNECT CONTROLLER':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
# Create a TCP socket (SOCK_STREAM)
controller_ip_addr = self.app_window.ui.Controller_IP_Addr_Edit.text()
controller_ip_port = int(self.app_window.ui.Controller_IP_Port_Edit.text())
try:
self.socket_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
text = 'test'
data = text.encode('ascii')
self.socket_udp.sendto(data, (controller_ip_addr, controller_ip_port))
data, address = self.socket_udp.recvfrom(MAX_BYTES)
text = data.decode('ascii')
print('The server {} replied {!r}'.format(address, text))
except socket.error as err:
print('Failed to crate a socket')
print('Reason: %s' %str(err))
# Update GLOBAL_MHL_ERROR here
else:
self.app_window.ui.Connect_Controller_Status_Edit.setText('Yes')
self.socket_udp.close()
finally:
pass
elif state_name == 'STOP CONTROL':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
pass
elif state_name == 'RUN CONTROL':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
pass
elif state_name == 'UPDATE CONTROL CONFIG':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
pass
elif state_name == 'UPDATE SETPOINT':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
pass
elif state_name == 'ERROR':
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
pass
else: # Undefined state
# Do something and update GLOBAL_MHL_ERROR, add create to MAIN_QUEUE if necessary, ...
pass
# Queue Manager: add state to the queue
# Check for GLOBAL_ERROR, then add ERROR state.
if GLOBAL_MHL_ERROR['ERROR']:
MAIN_QUEUE.appendleft({'STATE':'ERROR', 'DATA':GLOBAL_MHL_ERROR['DATA']})
# Chech global GLOBAL_EXIT.
if GLOBAL_EXIT:
MAIN_QUEUE.appendleft({'STATE':'EXIT', 'DATA':None})
print('---The message handling loop ends---')
return None
########## Run main.py
if __name__=="__main__":
app = QApplication(sys.argv)
app_window = AppWindow()
thread_message_handling_loop = MessageHandlingLoop(app_window)
thread_message_handling_loop.dameon = False
thread_message_handling_loop.start()
app_status = app.exec_()
thread_message_handling_loop.join() # Even though the thread runs an infinite while loop,
# the state 'EXIT' will return the function run().
sys.exit(app_status)