Skip to content

Commit

Permalink
Merge pull request #797 from OpenC3/python_interfaces
Browse files Browse the repository at this point in the history
Add Python streams and interfaces
  • Loading branch information
jmthomas authored Sep 7, 2023
2 parents 9346547 + 6e9c62b commit 78a1b18
Show file tree
Hide file tree
Showing 73 changed files with 8,825 additions and 456 deletions.
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
from openc3.script import *
from openc3.utilities.string import formatted
import tempfile

put_target_file("INST/test.txt", "this is a string test")
download_file("INST/test.txt") # download via path
file = get_target_file("INST/test.txt")
print(file.read)
file.rewind # rewind so download_file can read
print(file.read())
file.seek(0) # rewind so download_file can read
download_file(file) # download using file
file.delete
file.close() # closing deletes tempfile
delete_target_file("INST/test.txt")

save_file = Tempfile.new("test")
save_file = tempfile.NamedTemporaryFile(mode="w+t")
save_file.write("this is a Io test")
save_file.rewind
save_file.seek(0)
put_target_file("INST/test.txt", save_file)
save_file.delete
save_file.close() # Delete the tempfile
file = get_target_file("INST/test.txt")
print(file.read)
file.delete
print(file.read())
file.close()
delete_target_file("INST/test.txt")

put_target_file("INST/test.bin", "\x00\x01\x02\x03\xFF\xEE\xDD\xCC")
file = get_target_file("INST/test.bin")
print(file.read.formatted)
file.delete
delete_target_file("INST/test.bin")
# TODO: Binary not yet supported
# put_target_file("INST/test.bin", b"\x00\x01\x02\x03\xFF\xEE\xDD\xCC")
# file = get_target_file("INST/test.bin")
# print(formatted(file.read()))
# file.close()
# delete_target_file("INST/test.bin")
5 changes: 2 additions & 3 deletions openc3/lib/openc3/interfaces/protocols/burst_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,15 @@ def handle_sync_pattern

if found
if sync_index != 0
discard_length = @data[0..(sync_index - 1)].length
log_discard(discard_length, true)
log_discard(sync_index, true)
# Delete Data Before Sync Pattern
@data.replace(@data[sync_index..-1])
end
@sync_state = :FOUND
return nil

else # not found
log_discard(@data[0..sync_index].length, false)
log_discard(sync_index + 1, false)
# Delete Data Before and including first character of suspected sync Pattern
@data.replace(@data[(sync_index + 1)..-1])
next
Expand Down
2 changes: 1 addition & 1 deletion openc3/lib/openc3/interfaces/protocols/cobs_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def write_data(data, extra = nil)
result_data << data[0..253]
data = data[254..-1]
need_insert = false
else # index <= 254 or (index.nil? and data.length < 254)
else # index <= 253 or (index.nil? and data.length < 254)
if index
result_data << [index + 1].pack('C')
if index >= 1
Expand Down
2 changes: 1 addition & 1 deletion openc3/lib/openc3/interfaces/protocols/length_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def write_packet(packet)
# If the start of the length field is past what we discard, then the
# length field is inside the packet
if @length_bit_offset >= (@discard_leading_bytes * 8)
length = calculate_length(packet.buffer.length + @discard_leading_bytes)
length = calculate_length(packet.buffer(false).length + @discard_leading_bytes)
# Subtract off the discarded bytes since they haven't been added yet
# Adding bytes happens in the write_data method
offset = @length_bit_offset - (@discard_leading_bytes * 8)
Expand Down
2 changes: 1 addition & 1 deletion openc3/lib/openc3/interfaces/protocols/slip_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def initialize(
allow_empty_data = nil)

@start_char = ConfigParser.handle_nil(start_char)
@start_char = [Integer(end_char)].pack('C') if @start_char
@start_char = [Integer(start_char)].pack('C') if @start_char
@end_char = [Integer(end_char)].pack('C')
@esc_char = [Integer(esc_char)].pack('C')
@esc_end_char = [Integer(esc_end_char)].pack('C')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ def read_packet(packet)
result_packet.write(response_item_names[i], value)
rescue => error
handle_error("#{@interface ? @interface.name : ""}: Could not write value #{value} due to #{error.message}")
break
end
end

Expand Down
2 changes: 1 addition & 1 deletion openc3/lib/openc3/script/storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def get_target_file(path, original: false, scope: $openc3_scope)
if part == "targets_modified" and ENV['OPENC3_LOCAL_MODE']
local_file = OpenC3::LocalMode.open_local_file(path, scope: scope)
if local_file
OpenC3::Logger.info "Reading local #{scope}/#{path}"
OpenC3::Logger.info "Reading local #{scope}/#{part}/#{path}"
file = Tempfile.new('target', binmode: true)
file.filename = path
file.write(local_file.read)
Expand Down
43 changes: 25 additions & 18 deletions openc3/python/openc3/interfaces/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from openc3.utilities.logger import Logger
from openc3.logs.stream_log_pair import StreamLogPair

# TODO:
# require 'openc3/api/api'
# require 'openc3/utilities/secrets'

Expand Down Expand Up @@ -94,7 +95,7 @@ def disconnect(self):
def read_interface(self):
raise RuntimeError("read_interface not defined by Interface")

def write_interface(self, data):
def write_interface(self, data, extra=None):
raise RuntimeError("write_interface not defined by Interface")

# Retrieves the next packet from the interface.
Expand All @@ -114,16 +115,17 @@ def read(self):
# been received
if not first or len(self.read_protocols) <= 0:
# Read data for a packet
data = self.read_interface()
data, extra = self.read_interface()
if not data:
Logger.info(f"{self.name}: read_interface requested disconnect")
return None
else:
data = ""
data = b""
first = False

extra = None
for protocol in self.read_protocols:
data = protocol.read_data(data)
data, extra = protocol.read_data(data, extra)
if data == "DISCONNECT":
Logger.info(
f"{self.name}: Protocol {protocol.__class__.__name__} read_data requested disconnect"
Expand All @@ -134,7 +136,7 @@ def read(self):
if data == "STOP":
continue

packet = self.convert_data_to_packet(data)
packet = self.convert_data_to_packet(data, extra)

# Potentially modify packet
for protocol in self.read_protocols:
Expand Down Expand Up @@ -184,11 +186,11 @@ def write(self, packet):
if packet == "STOP":
return

data = self.convert_packet_to_data(packet)
data, extra = self.convert_packet_to_data(packet)

# Potentially modify packet data
for protocol in self.write_protocols:
data = protocol.write_data(data)
data, extra = protocol.write_data(data, extra)
if data == "DISCONNECT":
Logger.info(
f"{self.name}: Protocol {protocol.__class__.__name__} write_data requested disconnect"
Expand All @@ -199,11 +201,11 @@ def write(self, packet):
return

# Actually write out data if not handled by protocol:
self.write_interface(data)
self.write_interface(data, extra)

# Potentially block and wait for response
for protocol in self.write_protocols:
packet, data = protocol.post_write_interface(packet, data)
packet, data, extra = protocol.post_write_interface(packet, data, extra)
if packet == "DISCONNECT":
Logger.info(
f"{self.name}: Protocol {protocol.__class__.__name__} post_write_packet requested disconnect"
Expand All @@ -217,14 +219,14 @@ def write(self, packet):
# Writes preformatted data onto the interface. Malformed data may cause
# problems.
# self.param data [String] The raw data to send out the interface
def write_raw(self, data):
def write_raw(self, data, extra=None):
if not self.connected():
raise RuntimeError(f"Interface not connected for write_raw {self.name}")
if not self.write_raw_allowed:
raise RuntimeError(f"Interface not raw writable {self.name}")

with self._write():
self.write_interface(data)
self.write_interface(data, extra)

# Wrap all writes in a mutex and handle errors
@contextmanager
Expand Down Expand Up @@ -320,15 +322,20 @@ def set_option(self, option_name, option_values):
#
# self.param data [String] Raw packet data
# self.return [Packet] OpenC3 Packet with buffer filled with data
def convert_data_to_packet(self, data):
return Packet(None, None, "BIG_ENDIAN", None, data)
def convert_data_to_packet(self, data, extra):
packet = Packet(None, None, "BIG_ENDIAN", None, data)
packet.extra = extra
return packet

# Called to convert a packet into the data to send
#
# self.param packet [Packet] Packet to extract data from
# self.return data
# @param packet [Packet] Packet to extract data from
# @return data
def convert_packet_to_data(self, packet):
return packet.buffer # Copy buffer so logged command isn't modified
return (
packet.buffer,
packet.extra,
) # Copy buffer so logged command isn't modified

# Called to read data and manipulate it until enough data is
# returned. The definition of 'enough data' changes depending on the
Expand All @@ -338,7 +345,7 @@ def convert_packet_to_data(self, packet):
# method is called. Subclasses must implement this method.
#
# self.return [String] Raw packet data
def read_interface_base(self, data):
def read_interface_base(self, data, extra=None):
self.read_raw_data_time = datetime.now(timezone.utc)
self.read_raw_data = data
self.bytes_read += len(data)
Expand All @@ -351,7 +358,7 @@ def read_interface_base(self, data):
#
# self.param data [String] Raw packet data
# self.return [String] The exact data written
def write_interface_base(self, data):
def write_interface_base(self, data, extra=None):
self.written_raw_data_time = datetime.now(timezone.utc)
self.written_raw_data = data
self.bytes_written += len(data)
Expand Down
Loading

0 comments on commit 78a1b18

Please sign in to comment.