Skip to content

Commit 6c3a68f

Browse files
authored
Latest and greatest to Alicanto (#21)
* latest and greatest alicanto --------- Co-authored-by: jarwils <[email protected]>
1 parent d3f7582 commit 6c3a68f

File tree

1 file changed

+146
-134
lines changed

1 file changed

+146
-134
lines changed

src/pybennu/pybennu/executables/pybennu_alicanto.py

+146-134
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,12 @@ def __init__(self, end_dest):
6666
def send(self, message):
6767
""" Send message to Provider
6868
"""
69+
reply = None
6970
with timeout(10):
7071
self.connect()
72+
73+
#try:
74+
7175
# send update
7276
self._Client__socket.send_string(message+'\0') # must include null byte
7377
# get response
@@ -77,16 +81,25 @@ def send(self, message):
7781
data = reply[1]
7882

7983
if status == self._Client__kACK:
80-
print("I: ACK: "+data)
84+
logger.info(f"I: ACK -- {data}")
8185
#self.reply_handler(data)
8286
else:
83-
print("I: ERR -- %s" % msg)
87+
logger.error(f"I: ERR -- {msg}")
88+
89+
#finally:
90+
# logger.debug(f"DEBUG: Closing socket and context...")
91+
# self._Client__socket.close()
92+
# self._Client__context.term()
93+
# logger.debug(f"DEBUG: ... Closed socket and context")
8494

95+
try:
8596
self._Client__socket.close()
8697
self._Client__context.term()
98+
except:
99+
pass
87100

88-
return reply
89-
101+
return reply
102+
90103
class alicanto():
91104
def __init__(self, config, debug=False, exit_handler=None):
92105

@@ -101,16 +114,16 @@ def __init__(self, config, debug=False, exit_handler=None):
101114
self.lock = threading.Lock()
102115

103116
# Initialize system state
104-
self.state = {}
117+
self.state = dict()
105118
# Tag=>destination map
106-
self.dests = {}
119+
self.dests = dict()
107120
# Tag=>type map
108-
self.types = {}
109-
self.logic = {}
121+
self.types = dict()
122+
self.logic = dict()
110123
# Expression parser for logic
111124
self.parser = Parser()
112125
# Set of all tags
113-
self.tags = {}
126+
self.tags = dict()
114127

115128
############## Get counts from json ######################
116129
cfg = None
@@ -133,7 +146,7 @@ def __init__(self, config, debug=False, exit_handler=None):
133146

134147
# Diagnostics to confirm JSON config correctly added the required
135148
# endpoints and subscriptions
136-
self.endid = {}
149+
self.endid = dict()
137150
self.end_dests = []
138151
for i, endpoint in enumerate(cfg["endpoints"]):
139152
self.endid[i] = endpoint
@@ -149,7 +162,7 @@ def __init__(self, config, debug=False, exit_handler=None):
149162
# make end_dests elements unique
150163
self.end_dests = list(set(self.end_dests))
151164

152-
self.subid = {}
165+
self.subid = dict()
153166
self.sub_sources = []
154167
for i in range(0, self.sub_count):
155168
self.subid[i] = cfg["subscriptions"][i]
@@ -185,16 +198,6 @@ def __init__(self, config, debug=False, exit_handler=None):
185198
self.__sub_thread.daemon = True
186199
self.__sub_thread.start()
187200

188-
self.end_clients = {}
189-
for end_dest in self.end_dests:
190-
# Initialize bennu Client
191-
end_dest = end_dest.split('/')[0]
192-
try:
193-
self.end_clients[end_dest] = alicantoClient(end_dest)
194-
except:
195-
logger.error(f"\tError Initializing Client: {self.end_clients}")
196-
for key in list(self.end_clients.keys()):
197-
logger.info(f"End_client: {key}")
198201

199202
def run(self):
200203
############## Entering Execution Mode ##############################
@@ -212,17 +215,32 @@ def run(self):
212215
end_dest_tag = (full_end_dest.split('/')[1]
213216
if '/' in full_end_dest
214217
else full_end_dest)
218+
219+
logger.info("Reading initial value from client now...")
220+
try:
221+
client = alicantoClient(end_dest)
222+
except:
223+
logger.error(f"\tError Initializing Client: {client}")
224+
continue
225+
226+
try:
227+
reply = client.send("READ="+end_dest_tag)
228+
if not reply:
229+
continue
230+
except:
231+
logger.error(f"\tError Reading remote value")
232+
continue
233+
215234
try:
216-
self.end_clients[end_dest] = alicantoClient(end_dest)
217-
reply = self.end_clients[end_dest].send("READ="+end_dest_tag)
218235
value = reply[1].rstrip('\x00')
219236
self.endid[i]["value"] = value
220-
self.tag(full_end_dest, value)
237+
self.set_tag(full_end_dest, value)
221238
logger.debug(f"Initial Endpoints {end_name} / {end_dest}:{value} ")
222-
223239
except:
224-
logger.error(f"\tError Initializing Client: {self.end_clients}")
240+
logger.error(f"\tError Parsing response from Client")
225241
continue
242+
243+
logger.info("... Client value status initialized!")
226244

227245
########## Main co-simulation loop ####################################
228246
while True:
@@ -241,108 +259,112 @@ def run(self):
241259
if '/' in full_end_dest
242260
else full_end_dest)
243261

244-
if self.types[full_end_name] == 'float' or self.types[full_end_name] == 'double':
245-
if not math.isclose(float(self.tag(full_end_name)), float(self.tag(full_end_dest))):
246-
#Handle Logic
247-
if self.logic[full_end_dest] is not None:
248-
expr = self.parser.parse(self.logic[full_end_dest])
249-
'''
250-
# Assign variables
251-
vars = {}
252-
for var in expr.variables():
253-
vars[var] = self.tag(var)
254-
'''
255-
i = 0
256-
# Assign vars not working, so assign token manually
257-
for token in expr.tokens:
258-
for search_tag in self.tags:
259-
if token.toString() == search_tag:
260-
expr.tokens[i].number_ = self.tag(token.toString())
261-
i += 1
262-
# Evaluate expression
263-
value = expr.evaluate(vars)
264-
value = str(value).lower()
265-
if value != self.tag(full_end_dest):
266-
logger.debug(f"\tLOGIC: {full_end_dest.strip()}={self.logic[full_end_dest]} ----> {value}")
267-
# Assign new tag value
268-
self.tag(full_end_dest, value)
269-
# Skip if value is unchanged
270-
elif value == self.tag(full_end_dest):
271-
continue
272-
273-
try:
274-
self.end_clients[end_dest] = alicantoClient(end_dest)
275-
if self.logic[full_end_dest] is not None:
276-
self.end_clients[end_dest].write_analog_point(end_dest_tag, self.tag(full_end_dest))
277-
else:
278-
self.end_clients[end_dest].write_analog_point(end_dest_tag, self.tag(full_end_name))
279-
time.sleep(0.5)
280-
reply = self.end_clients[end_dest].send("READ="+end_dest_tag)
281-
value = reply[1].rstrip('\x00')
282-
self.tag(full_end_dest, value)
283-
except:
284-
logger.error(f"\tError Initializing Client: {self.end_clients}")
285-
continue
286-
elif self.types[full_end_name] == 'bool':
287-
if str(self.tag(full_end_name)).lower() != str(self.tag(full_end_dest)).lower():
288-
#Handle Logic
289-
if self.logic[full_end_dest] is not None:
290-
expr = self.parser.parse(self.logic[full_end_dest])
291-
'''
292-
# Assign variables
293-
vars = {}
294-
for var in expr.variables():
295-
vars[var] = self.tag(var)
296-
'''
297-
i = 0
298-
# Assign vars not working, so assign token manually
299-
for token in expr.tokens:
300-
for search_tag in self.tags:
301-
if token.toString() == search_tag:
302-
expr.tokens[i].number_ = bool(self.tag(token.toString()))
303-
i += 1
304-
# Evaluate expression
305-
value = expr.evaluate(vars)
306-
value = str(value)
307-
if value != self.tag(full_end_dest):
308-
logger.debug(f"\tLOGIC: {full_end_dest.strip()}={self.logic[full_end_dest]} ----> {value}")
309-
# Assign new tag value
310-
self.tag(full_end_dest, value)
311-
# Skip if value is unchanged
312-
elif value == self.tag(full_end_dest):
313-
continue
314-
try:
315-
self.end_clients[end_dest] = alicantoClient(end_dest)
316-
if self.logic[full_end_dest] is not None:
317-
self.end_clients[end_dest].write_digital_point(end_dest_tag, eval(self.tag(full_end_dest)))
318-
else:
319-
self.end_clients[end_dest].write_digital_point(end_dest_tag, eval(self.tag(full_end_name)))
320-
time.sleep(0.5)
321-
reply = self.end_clients[end_dest].send("READ="+end_dest_tag)
322-
value = reply[1].rstrip('\x00')
323-
self.tag(full_end_dest, value)
324-
except:
325-
logger.error(f"\tError Initializing Client: {self.end_clients}")
326-
continue
262+
these_types = self.types[full_end_name]
263+
264+
# If we don't need to update the value, escape early
265+
if (these_types == 'float' or these_types == 'double') and (math.isclose(float(self.get_tag(full_end_name)), float(self.get_tag(full_end_dest)))):
266+
#logger.debug(f"SKIP: No need to update {self.get_tag(full_end_name)} vs {self.get_tag(full_end_dest)}")
267+
continue
268+
true_set = (True, 'True', 'true', '1', 1)
269+
if (these_types == 'bool') and ((self.get_tag(full_end_name) in true_set) == (self.get_tag(full_end_dest) in true_set)):
270+
#logger.debug(f"SKIP: No need to update {self.get_tag(full_end_name)} vs {self.get_tag(full_end_dest)}")
271+
continue
272+
273+
# Handle the case where there is logic involved
274+
if self.logic[full_end_dest] is not None:
275+
expr = self.parser.parse(self.logic[full_end_dest])
276+
277+
# Assign variables
278+
these_vars = dict()
279+
280+
'''
281+
for v in expr.variables():
282+
t = self.get_tag(v)
283+
if these_types == 'bool':
284+
t = bool(t)
285+
286+
these_vars[v] = t
287+
288+
'''
289+
# Automatic variable parsing with expression parser not working, so assign token manually
290+
for i,token in enumerate(expr.tokens):
291+
if token.toString() in self.tags:
292+
t = self.get_tag(token.toString())
293+
if these_types == 'bool':
294+
t = bool(t)
295+
296+
expr.tokens[i].number_ = t
297+
298+
# Evaluate expression
299+
value = expr.evaluate(these_vars)
300+
value = str(value)
301+
if value != self.get_tag(full_end_dest):
302+
self.set_tag(full_end_dest, value)
303+
else:
304+
continue
305+
306+
tag_ptr = full_end_dest
307+
308+
else:
309+
tag_ptr = full_end_name
310+
311+
312+
# Send update
313+
logger.info(f"Sending value update...")
314+
try:
315+
client = alicantoClient(end_dest)
316+
except:
317+
logger.error(f"\tError Initializing Client: {client}")
318+
continue
319+
320+
try:
321+
if these_types == 'float' or these_types == 'double':
322+
client.write_analog_point(end_dest_tag, self.get_tag(tag_ptr))
323+
elif these_types == 'bool':
324+
client.write_digital_point(end_dest_tag, eval(self.get_tag(tag_ptr)))
325+
except:
326+
logger.error(f"\tError Writing value to remote Client")
327+
328+
time.sleep(0.5)
329+
330+
try:
331+
reply = client.send("READ="+end_dest_tag)
332+
if not reply:
333+
continue
334+
except:
335+
logger.error(f"\tError Reading remote value")
336+
continue
337+
338+
try:
339+
value = reply[1].rstrip('\x00')
340+
self.set_tag(full_end_dest, value)
341+
except:
342+
logger.error(f"\tError Parsing response from Client")
343+
continue
344+
345+
logger.info(f"... Update sent!")
346+
327347

328348
def publish_state(self):
329349
logger.info("=================== DATA ===================")
330350
for tag in self.tags:
331-
logger.info(f"{tag:<30} --- {self.tag(tag):}")
351+
logger.info(f"{tag:<30} --- {self.get_tag(tag):}")
332352
logger.info("============================================")
333353

334354
def get_type(self, tag):
335355
return self.types[tag]
336356

337-
def tag(self, tag, value=None):
357+
def get_tag(self, tag):
358+
with self.lock:
359+
if tag in self.state:
360+
return self.state[tag]
361+
else:
362+
return False if self.get_type(tag) == 'bool' else 0
363+
364+
def set_tag(self, tag, value=None):
338365
with self.lock:
339366
if value is not None:
340367
self.state[tag] = value
341-
else:
342-
if tag in self.state:
343-
return self.state[tag]
344-
else:
345-
return False if self.get_type(tag) == 'bool' else 0
346368

347369
def _subscription_handler(self, message):
348370
"""Receive Subscription message
@@ -359,10 +381,8 @@ def _subscription_handler(self, message):
359381
sub_source = threading.current_thread().name
360382

361383
for point in points:
362-
if not point:
384+
if not point or len(point) <= 1:
363385
continue
364-
if point == "":
365-
continue
366386

367387
try:
368388
tag = point.split(':')[0]
@@ -386,21 +406,13 @@ def _subscription_handler(self, message):
386406
field = 'value'
387407

388408
if field == 'value':
389-
if not math.isclose(float(self.tag(full_tag)), value):
390-
self.tag(full_tag, value)
391-
logger.debug("UPDATE NOW: "+full_tag)
392-
logger.debug("New value: "+str(value))
393-
else:
394-
continue
409+
if not math.isclose(float(self.get_tag(full_tag)), value):
410+
self.set_tag(full_tag, value)
411+
logger.debug(f"UPDATE NOW: {full_tag} = {value}")
395412
elif field == 'status':
396-
if self.tag(full_tag) != value:
397-
self.tag(full_tag, value)
398-
logger.debug("UPDATE NOW: "+full_tag)
399-
logger.debug("New value: "+str(value))
400-
else:
401-
continue
402-
else:
403-
continue
413+
if self.get_tag(full_tag) != value:
414+
self.set_tag(full_tag, value)
415+
logger.debug(f"UPDATE NOW: {full_tag} = {value}")
404416

405417
def ctrl_exit_handler(self, signal, frame):
406418
logger.info("SIGINT or CTRL-C detected. Exiting gracefully")
@@ -423,4 +435,4 @@ def main():
423435
try:
424436
main()
425437
except KeyboardInterrupt:
426-
sys.exit()
438+
sys.exit()

0 commit comments

Comments
 (0)