def update_sensor_source(self, sensor, source): priority = source_priority[source] watch = priority < signalk_priority # translate from pypilot -> signalk if watch: watch = self.period.value for signalk_path_conversion, pypilot_path in signalk_table[sensor].items(): if type(pypilot_path) == type({}): for signalk_key, pypilot_key in pypilot_path.items(): pypilot_path = sensor + '.' + pypilot_key if pypilot_path in self.last_values: del self.last_values[pypilot_path] self.client.watch(pypilot_path, watch) else: # remove any last values from this sensor pypilot_path = sensor + '.' + pypilot_path if pypilot_path in self.last_values: del self.last_values[pypilot_path] self.client.watch(pypilot_path, watch) subscribe = priority >= signalk_priority # prevent duplicating subscriptions if self.subscribed[sensor] == subscribe: return self.subscribed[sensor] = subscribe if not subscribe: #signalk can't unsubscribe by path!?!?! subscription = {'context': '*', 'unsubscribe': [{'path': '*'}]} debug('signalk unsubscribe', subscription) self.ws.send(pyjson.dumps(subscription)+'\n') signalk_sensor = signalk_table[sensor] if subscribe: # translate from signalk -> pypilot subscriptions = [] for signalk_path_conversion in signalk_sensor: signalk_path, signalk_conversion = signalk_path_conversion if signalk_path in self.signalk_last_msg_time: del self.signalk_last_msg_time[signalk_path] subscriptions.append({'path': signalk_path, 'minPeriod': self.period.value*1000, 'format': 'delta', 'policy': 'instant'}) self.subscriptions += subscriptions else: # remove this subscription and resend all subscriptions debug('signalk remove subs', signalk_sensor, self.subscriptions) subscriptions = [] for subscription in self.subscriptions: for signalk_path_conversion in signalk_sensor: signalk_path, signalk_conversion = signalk_path_conversion if subscription['path'] == signalk_path: break else: subscriptions.append(subscription) self.subscriptions = subscriptions self.signalk_last_msg_time = {} subscription = {'context': 'vessels.self'} subscription['subscribe'] = subscriptions debug('signalk subscribe', subscription) self.ws.send(pyjson.dumps(subscription)+'\n')
def onconnected(self): #print('connected to pypilot server', time.time()) self.last_values_list = False # write config if connection succeeds try: file = open(self.configfilename, 'w') file.write(pyjson.dumps(self.config) + '\n') file.close() self.write_config = False except IOError: print('failed to write config file:', self.configfilename) except Exception as e: print('Exception writing config file:', self.configfilename, e) self.connection = LineBufferedNonBlockingSocket( self.connection_in_progress, self.config['host']) self.connection_in_progress = False self.poller = select.poll() self.poller.register(self.connection.socket, select.POLLIN) self.wwatches = {} for name, value in self.watches.items(): self.wwatches[name] = value # resend watches self.values.onconnected()
def pypilotClientFromArgs(values, period=True, host=False): client = pypilotClient(host) if not client.connect(True): print('failed to connect to', host) exit(1) # set any value specified with path=value watches = {} sets = False for arg in values: if '=' in arg: name, value = arg.split('=', 1) try: # make string if it won't load pyjson.loads(value) except: value = pyjson.dumps(value) client.send(name + '=' + value + '\n') sets = True watches[name] = True else: name = arg watches[name] = period if sets: client.poll(1) #time.sleep(.2) # is this needed? for name in watches: client.watch(name, watches[name]) return client
def commit(self, state): """ Write out the state in JSON format to a temporary file and rename it into place """ with tempfile.NamedTemporaryFile() as f: f.write(pyjson.dumps(state)) f.flush() shutil.copyfile(f.name, self._state_file)
def success(name, device): global probes filename = pypilot_dir + name + 'device' print('serialprobe success:', filename, device) try: file = open(filename, 'w') file.write(pyjson.dumps(device) + '\n') file.close() except: print('serialprobe failed to record device', name)
def send(self, value, block=False): t0 = time.time() try: data = pyjson.dumps(value) self.write(data + '\n') t1 = time.time() if t1 - t0 > .02: print('too long', t1 - t0, self.name, len(data)) return True except Exception as e: print('failed to encode data socket!', self.name, e) return False
def success(name, device): global probes filename = pypilot_dir + name + 'device' print('serialprobe ' + _('success') + ':', filename, device) probes[name]['lastworking'] = device try: file = open(filename, 'w') file.write(pyjson.dumps(device) + '\n') file.close() except: print('serialprobe ' + _('failed to record device'), name)
def get_msg(self): if not self.msg or self.msg == 'new': msg = 'values={' notsingle = False for name in self.values: if name in self.internal: continue info = self.values[name].info if not info: # placeholders that are watched continue if notsingle: msg += ',' msg += '"' + name + '":' + pyjson.dumps(info) notsingle = True self.msg = msg + '}\n' #print('values len', len(self.msg)) return self.msg
def set(self, msg, connection): if isinstance(connection, LineBufferedNonBlockingSocket): connection.write('error=remote sockets not allowed to register\n') return n, data = msg.rstrip().split('=', 1) values = pyjson.loads(data) for name in values: info = values[name] if name in self.values: value = self.values[name] if value.connection: connection.write('error=value already held: ' + name + '\n') continue value.connection = connection value.info = info # update info value.watching = False if value.msg: connection.write(value.get_msg()) # send value value.calculate_watch_period() self.msg = 'new' continue value = pypilotValue(self, name, info, connection) if 'persistent' in info and info['persistent']: # when a persistant value is missing from pypilot.conf value.calculate_watch_period() #info['persistent'] = 'new' ??? if name in self.persistent_data: print('IS THIS POSSIBLE TO HIT?????') v = self.persistent_data[name] if isinstance(v, numbers.Number): v = float(v) # convert any numeric to floating point value.set(v, connection) # set persistent value self.persistent_values[name] = value self.values[name] = value self.msg = 'new' msg = False # inform watching clients of updated values for watch in self.awatches: for c in watch.connections: if c != connection: if not msg: msg = 'values=' + pyjson.dumps(values) + '\n' c.write(msg)
def send_signalk(self): # see if we can produce any signalk output from the data we have read updates = [] for sensor in signalk_table: if sensor != 'imu' and (not sensor in self.last_sources or\ source_priority[self.last_sources[sensor]]>=signalk_priority): #debug('signalk skip send from priority', sensor) continue for signalk_path_conversion, pypilot_path in signalk_table[ sensor].items(): signalk_path, signalk_conversion = signalk_path_conversion if type(pypilot_path) == type( {}): # single path translates to multiple pypilot keys = self.last_values_keys[signalk_path] # store keys we need for this signalk path in dictionary for signalk_key, pypilot_key in pypilot_path.items(): key = sensor + '.' + pypilot_key if key in self.last_values: keys[key] = self.last_values[key] # see if we have the keys needed v = {} for signalk_key, pypilot_key in pypilot_path.items(): key = sensor + '.' + pypilot_key if not key in keys: break v[signalk_key] = keys[key] * signalk_conversion else: updates.append({'path': signalk_path, 'value': v}) self.last_values_keys[signalk_path] = {} else: key = sensor + '.' + pypilot_path if key in self.last_values: v = self.last_values[key] * signalk_conversion updates.append({'path': signalk_path, 'value': v}) if updates: # send signalk updates msg = {'updates': [{'$source': 'pypilot', 'values': updates}]} debug('signalk updates', msg) try: self.ws.send(pyjson.dumps(msg) + '\n') except Exception as e: print('signalk failed to send', e) self.disconnect_signalk()
def send(self, value, block=False): if not self.pollout.poll(0): if not self.sendfailok: print('failed send', self.name) t0 = time.time() try: data = pyjson.dumps(value) + '\n' os.write(self.w, data.encode()) t1 = time.time() self.flush() t2 = time.time() if t2-t0 > .024: print('too long send nonblocking pipe', t1-t0, t2-t1, self.name, len(data)) return True except Exception as e: if not self.sendfailok: print('failed to encode data pipe!', self.name, e) return False
def send(self, value, block=False, maxdt=.025): if 0: if not self.pollout.poll(0): if not self.sendfailok: print('failed poll send', self.name) t0 = time.monotonic() try: data = pyjson.dumps(value) + '\n' data = data.encode() t1 = time.monotonic() os.write(self.w, data) t2 = time.monotonic() if t2 - t0 > maxdt: print('too long send nonblocking pipe', t1 - t0, t2 - t1, self.name, len(data)) return True except Exception as e: print("failed send ex", t0, time.monotonic(), e) if not self.sendfailok: print('failed to encode data pipe!', self.name, e) return False
def pypilotClientFromArgs(values, period=True, host=False): client = pypilotClient(host) if host: client.probed = True # dont probe if not client.connect(True): print(_('failed to connect to'), host) if not host and client.probewait(5): if not client.connect(True): print(_('failed to connect to'), client.config['host']) exit(1) else: print(_('no pypilot server found')) exit(1) # set any value specified with path=value watches = {} sets = False for arg in values: if '=' in arg: name, value = arg.split('=', 1) try: # make string if it won't load pyjson.loads(value) except: value = pyjson.dumps(value) client.send(name + '=' + value + '\n') sets = True watches[name] = True else: name = arg watches[name] = period if sets: client.poll(1) for name in watches: client.watch(name, watches[name]) return client
def set(self, msg, connection): name, data = msg.rstrip().split('=', 1) values = pyjson.loads(data) for name in values: info = values[name] if name in self.values: value = self.values[name] if value.connection: connection.write('error=value already held: ' + name + '\n') continue value.connection = connection value.info = info # update info value.watching = False if value.msg: connection.write(value.get_msg()) # send value value.calculate_watch_period() self.msg = 'new' continue value = pypilotValue(self, name, info, connection) if 'persistent' in info and info['persistent']: value.calculate_watch_period() if name in self.persistent_data: v = self.persistent_data[name] if isinstance(v, numbers.Number): v = float(v) # convert any numeric to floating point value.set(v, connection) # set persistent value self.values[name] = value self.msg = 'new' msg = False # inform watching clients of updated values for watch in self.awatches: for c in watch.connections: if c != connection: if not msg: msg = 'values=' + pyjson.dumps(values) + '\n' c.write(msg)
def load(self): self.persistent_data = {} try: self.load_file(open(default_persistent_path)) except Exception as e: print('failed to load', default_persistent_path, e) # log failing to load persistent data persist_fail = os.getenv('HOME') + '/.pypilot/persist_fail' file = open(persist_fail, 'a') file.write(str(time.time()) + ' ' + str(e) + '\n') file.close() try: self.load_file(open(default_persistent_path + '.bak')) return except Exception as e: print('backup data failed as well', e) return # backup persistent_data if it loaded with success file = open(default_persistent_path + '.bak', 'w') file.write(pyjson.dumps(self.persistent_data) + '\n') file.close()
def send_signalk(self): # see if we can produce any signalk output from the data we have read updates = [] for sensor in signalk_table: for signalk_path_conversion, pypilot_path in signalk_table[ sensor].items(): signalk_path, signalk_conversion = signalk_path_conversion if signalk_path in self.signalk_msgs: continue if type(pypilot_path) == type( {}): # single path translates to multiple pypilot v = {} for signalk_key, pypilot_key in pypilot_path.items(): key = sensor + '.' + pypilot_key if not key in self.last_values: break v[signalk_key] = self.last_values[ key] * signalk_conversion else: updates.append({'path': signalk_path, 'value': v}) self.signalk_msgs[signalk_path] = True else: key = sensor + '.' + pypilot_path if key in self.last_values: v = self.last_values[key] * signalk_conversion updates.append({'path': signalk_path, 'value': v}) self.signalk_msgs[signalk_path] = True if updates: # send signalk updates msg = {'updates': [{'$source': 'pypilot', 'values': updates}]} #print('signalk updates', msg) try: self.ws.send(pyjson.dumps(msg) + '\n') except Exception as e: print('signalk failed to send', e) self.disconnect_signalk()
def save_calibration(self): file = open(Servo.calibration_filename, 'w') file.write(pyjson.dumps(self.calibration))
def poll(self, timeout=0): if not self.connection: if self.connection_in_progress: events = self.poller_in_progress.poll(0) if events: fd, flag = events.pop() if not (flag & select.POLLOUT): # hung hup self.connection_in_progress.close() self.connection_in_progress = False return self.onconnected() return else: if not self.connect(False): time.sleep(timeout) return # inform server of any watches we have changed if self.wwatches: self.connection.write('watch=' + pyjson.dumps(self.wwatches) + '\n') #print('watch', watches, self.wwatches, self.watches) self.wwatches = {} # send any delayed watched values self.values.send_watches() if self.connection.fileno(): # flush output self.connection.flush() try: events = self.poller.poll(int(1000 * timeout)) except Exception as e: print('exception polling', e, os.getpid()) self.disconnect() return if not events: return # no data ready fd, flag = events.pop() if not (flag & select.POLLIN) or (self.connection and not self.connection.recvdata()): # other flags indicate disconnect self.disconnect() # recv returns 0 means connection closed return # read incoming data line by line while True: t0 = time.monotonic() line = self.connection.readline() if not line: return try: name, data = line.rstrip().split('=', 1) if name == 'error': print('server error:', data) continue value = pyjson.loads(data) except ValueError as e: print('client value error:', line, e) #raise Exception continue except Exception as e: print('invalid message from server:', line, e) raise Exception() if name in self.values.values: # did this client register this value self.values.values[name].set(value) else: self.received.append((name, value)) # remote value
def get_msg(self): ret = pyjson.dumps(self.wvalues) self.wvalues = {} return ret
def poll(self, timeout=0): # server is in subprocess if self.process != 'main process': if not self.process: self.init_process() return t0 = time.monotonic() if t0 >= self.values.persistent_timeout: self.values.store() dt = time.monotonic() - t0 if dt > .1: print('persistent store took too long!', time.monotonic() - t0) return if timeout: timeout *= 1000 # milliseconds timeout = .1 events = self.poller.poll(timeout) while events: event = events.pop() fd, flag = event connection = self.fd_to_connection[fd] if connection == self.server_socket: connection, address = connection.accept() if len(self.sockets) == max_connections: print('pypilot server: max connections reached!!!', len(self.sockets)) self.RemoveSocket(self.sockets[0]) # dump first socket?? socket = LineBufferedNonBlockingSocket(connection, address) print('server add socket', socket.address) self.sockets.append(socket) fd = socket.fileno() socket.cwatches = { 'values': True } # server always watches client values self.fd_to_connection[fd] = socket self.poller.register(fd, select.POLLIN) elif flag & (select.POLLHUP | select.POLLERR | select.POLLNVAL): if not connection in self.sockets: print('internal pipe closed, server exiting') exit(0) self.RemoveSocket(connection) elif flag & select.POLLIN: if fd in self.fd_to_pipe: if not connection.recvdata(): continue line = connection.readline( ) # shortcut since poll indicates data is ready while line: self.values.HandlePipeRequest(line, connection) line = connection.readline() continue if not connection.recvdata(): self.RemoveSocket(connection) continue while True: line = connection.readline() if not line: break try: self.values.HandleRequest(line, connection) except Exception as e: connection.write('error=invalid request: ' + line) try: print('invalid request from connection', e, line) except Exception as e2: print('invalid request has malformed string', e, e2) if not self.multiprocessing: # these pipes are not pollable as they are implemented as a simple buffer for pipe in self.pipes: while True: line = pipe.readline() if not line: break self.values.HandlePipeRequest(line, pipe) # send periodic watches self.values.send_watches() # send watches for connection in self.sockets + self.pipes: if connection.cwatches: connection.write('watch=' + pyjson.dumps(connection.cwatches) + '\n') connection.cwatches = {} # flush all sockets for socket in self.sockets: socket.flush() while True: for socket in self.sockets: if not socket.socket: print('server socket closed from flush!!') self.RemoveSocket(socket) break else: break for pipe in self.pipes: pipe.flush()
def get_msg(self): return pyjson.dumps(self.value)