saleae 0.1.0

a rust library for interacting with saleae devices
Documentation
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <style>html, body {
  margin: 0;
  padding: 0;
}

.app {
  margin: 10px;
  padding: 0;
}

.files-list {
  margin: 10px 0 0;
  width: 100%;
  border-collapse: collapse;
}
.files-list__head {
  border: 1px solid #999;
}
.files-list__head > tr > th {
  padding: 10px;
  border: 1px solid #999;
  text-align: left;
  font-weight: normal;
  background: #ddd;
}
.files-list__body {
}
.files-list__file {
  cursor: pointer;
}
.files-list__file:hover {
  background: #ccf;
}
.files-list__file > td {
  padding: 10px;
  border: 1px solid #999;
}
.files-list__file > td:first-child::before {
  content: '\01F4C4';
  margin-right: 1em;
}
.files-list__file_low {
  background: #fcc;
}
.files-list__file_medium {
  background: #ffc;
}
.files-list__file_high {
  background: #cfc;
}
.files-list__file_folder > td:first-child::before {
  content: '\01F4C1';
  margin-right: 1em;
}

.file-header {
  border: 1px solid #999;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.file-header__back {
  margin: 10px;
  cursor: pointer;
  flex-shrink: 0;
  flex-grow: 0;
  text-decoration: underline;
  color: #338;
}

.file-header__name {
  margin: 10px;
  flex-shrink: 2;
  flex-grow: 2;
}

.file-header__stat {
  margin: 10px;
  flex-shrink: 0;
  flex-grow: 0;
}

.file-content {
  margin: 10px 0 0;
  border: 1px solid #999;
  padding: 10px;
}

.code-line {
  margin: 0;
  padding: 0.3em;
  height: 1em;
}
.code-line_covered {
  background: #cfc;
}
.code-line_uncovered {
  background: #fcc;
}
</style>
</head>
<body>
    <div id="root"></div>
    <script>var data = {"files":[{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","client.rs"],"content":"//! This module defines the client data structure - the main entry point of communication\n//! to the saleae\n\nuse anyhow::Result;\nuse std::io::prelude::{Read, Write};\nuse std::io::{BufReader, BufWriter};\nuse std::net::TcpStream;\n\nuse crate::device::ConnectedDevice;\nuse crate::performance::PerformanceOption;\nuse crate::request::Request;\nuse crate::response::Response;\nuse crate::samplerate::SampleRate;\n\n#[faux::create]\n#[derive(Debug)]\npub struct Connection {\n    stream: TcpStream,\n}\n\n#[faux::methods]\nimpl Connection {\n    pub fn new(ip_port: \u0026str) -\u003e Self {\n        Connection {\n            stream: TcpStream::connect(ip_port).unwrap(),\n        }\n    }\n\n    pub fn general_recieve_ack(\u0026mut self) -\u003e Result\u003cbool\u003e {\n        let r: String = std::str::from_utf8(\u0026self.read_line()?)?.to_string();\n        Ok(Response::verify_ack(\u0026r))\n    }\n\n    pub fn general_recieve_message(\u0026mut self) -\u003e Result\u003cString\u003e {\n        let msg: String = std::str::from_utf8(\u0026self.read_line()?)?.to_string();\n        Response::verify_ack(\u0026msg);\n        Ok(msg)\n    }\n\n    fn read_line(\u0026mut self) -\u003e Result\u003cVec\u003cu8\u003e\u003e {\n        let mut reader = BufReader::new(\u0026self.stream);\n        let mut buf = [0; 500];\n        let len = reader.read(\u0026mut buf)?;\n        if len \u003c 1 {\n            panic!(\"read buffer len \u003c 0\");\n        }\n        Ok(buf[..len].to_vec())\n    }\n\n    //TODO Support for parameters\n    pub fn run_command(\u0026mut self, command: \u0026str) -\u003e Result\u003c()\u003e {\n        let mut writer = BufWriter::new(\u0026self.stream);\n        let len = writer.write(command.as_bytes()).unwrap();\n        if len \u003c 1 {\n            panic!(\"write buffer len \u003c 0\");\n        }\n        Ok(())\n    }\n}\n\n#[derive(Debug)]\n/// Main interface for communication to Saleae Logic\npub struct Client {\n    /// tcp stream with connection to saleae\n    connection: Connection,\n}\n\n/// Constructor\nimpl Client {\n    /// constructor\n    //TODO make this create a connection from a string\n    pub fn new(connection: Connection) -\u003e Result\u003cClient\u003e {\n        Ok(Client { connection })\n    }\n}\n\n/// Interface for setting and getting Logic information\nimpl Client {\n    /// Set a trigger of channels\n    /// TODO create trigger methods/structs/tests\n\n    pub fn set_num_samples(\u0026mut self, num: u32) -\u003e Result\u003cbool\u003e {\n        self.connection\n            .run_command(\u0026format!(\"set_num_samples, {}\\0\", num))?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    pub fn get_num_samples(\u0026mut self) -\u003e Result\u003cu32\u003e {\n        self.connection.run_command(\"get_num_samples\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        if Response::verify_ack(\u0026response) {\n            Ok(Response::parse_num_samples(\u0026Response::remove_ack(\n                \u0026response,\n            )))\n        } else {\n            Err(anyhow!(\"No ACK found\"))\n        }\n    }\n\n    /// Set the capture duration to a length of time\n    pub fn set_capture_seconds(\u0026mut self, seconds: f32) -\u003e Result\u003cbool\u003e {\n        self.connection\n            .run_command(\u0026format!(\"set_capture_seconds, {}\\0\", seconds))?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    /// Set sample rate of saleae\n    ///\n    /// Note: Make sure to run `get_all_sample_rates` and set it from a available\n    /// sample rate\n    pub fn set_sample_rate(\u0026mut self, rate: \u0026SampleRate) -\u003e Result\u003cbool\u003e {\n        self.connection.run_command(\u0026format!(\n            \"set_sample_rate, {}, {}\\0\",\n            rate.DigitalSampleRate, rate.AnalogSampleRate\n        ))?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    pub fn get_sample_rate(\u0026mut self) -\u003e Result\u003cSampleRate\u003e {\n        self.connection.run_command(\"get_sample_rate\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        Ok(Response::parse_get_sample_rate(\u0026Response::remove_ack(\n            \u0026response,\n        )))\n    }\n\n    pub fn get_all_sample_rates(\u0026mut self) -\u003e Result\u003cVec\u003cSampleRate\u003e\u003e {\n        self.connection.run_command(\"get_all_sample_rates\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        Ok(Response::parse_get_all_sample_rates(\u0026Response::remove_ack(\n            \u0026response,\n        )))\n    }\n\n    /// Return current performance level of Logic\n    pub fn get_performance(\u0026mut self) -\u003e Result\u003cu8\u003e {\n        self.connection.run_command(\"get_performance\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        Ok(Response::parse_performance(\u0026Response::remove_ack(\n            \u0026response,\n        )))\n    }\n\n    /// Set the performance value, controlling the USB traffic and quality\n    pub fn set_performance(\u0026mut self, perf: PerformanceOption) -\u003e Result\u003cbool\u003e {\n        let input = String::from(\u0026format!(\"set_performance, {}\\0\", perf as i32));\n        self.connection.run_command(\u0026input)?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    //TODO get_capture_pretrigger_buffer_size\n\n    /// Return current connected devices of Logic\n    pub fn get_connected_devices(\u0026mut self) -\u003e Result\u003cVec\u003cConnectedDevice\u003e\u003e {\n        self.connection.run_command(\"get_connected_devices\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        Ok(Response::parse_connected_devices(\u0026Response::remove_ack(\n            \u0026response,\n        )))\n    }\n\n    /// Find index of device from the list of devices connected to Saleae\n    ///\n    /// Get current index of connected devices and find equal device to parameter.\n    /// Send that device as active device to logic.\n    ///\n    /// Note: Indices start at 1, not 0\n    /// TODO: test with multiple saleae\n    pub fn select_active_device(\u0026mut self, device: ConnectedDevice) -\u003e Result\u003cbool\u003e {\n        let b = self\n            .get_connected_devices()\n            .unwrap()\n            .into_iter()\n            .position(|a| a == device);\n        self.connection\n            .run_command(\u0026format!(\"select_active_device, {}\", b.unwrap() + 1))?;\n        // Weirdly doesn't return an ACK\n        Ok(true)\n    }\n\n    /// Return current active device of Logic\n    pub fn get_active_device(\u0026mut self) -\u003e Result\u003cConnectedDevice\u003e {\n        self.connection.run_command(\"get_connected_devices\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        Ok(\n            Response::parse_connected_devices(\u0026Response::remove_ack(\u0026response))\n                .into_iter()\n                .find(|a| a.is_active)\n                .unwrap(),\n        )\n    }\n\n    /// Parse the get active channels command into tuples of digital and analog\n    /// channels that are current\n    pub fn get_active_channels(\u0026mut self) -\u003e Result\u003cVec\u003cVec\u003cu8\u003e\u003e\u003e {\n        self.connection.run_command(\"get_active_channels\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        Ok(Response::parse_get_active_channels(\u0026Response::remove_ack(\n            \u0026response,\n        ))?)\n    }\n\n    /// Set the active channels for the Logic program\n    ///\n    /// # Example\n    /// TODO add get_active_channels\n    pub fn set_active_channels(\n        \u0026mut self,\n        digital_channels: \u0026[u8],\n        analog_channels: \u0026[u8],\n    ) -\u003e Result\u003cbool\u003e {\n        self.connection\n            .run_command(\u0026Request::prepare_set_active_channels(\n                \u0026digital_channels,\n                \u0026analog_channels,\n            )?)?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    /// Reset Active Channel\n    pub fn reset_active_channels(\u0026mut self) -\u003e Result\u003cbool\u003e {\n        self.connection.run_command(\"reset_active_channels\\0\")?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    //TODO get_digital_voltage_options OR get_full_scale_voltage_range\n    //TODO set_full_scale_voltage_range\n\n    /// Start Capture, without wating for ack/nack\n    pub fn start_capture(\u0026mut self) -\u003e Result\u003cbool\u003e {\n        self.connection.run_command(\"capture\\0\")?;\n        Ok(true)\n        // Doesn't return ACK\n    }\n\n    /// Start Capture, then wait until ack\n    pub fn start_capture_block_until_finished(\u0026mut self) -\u003e Result\u003cbool\u003e {\n        self.start_capture()?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    /// Check if processing is complete\n    pub fn is_processing_complete(\u0026mut self) -\u003e Result\u003cbool\u003e {\n        self.connection.run_command(\"is_processing_complete\\0\")?;\n        let response = self.connection.general_recieve_message()?;\n        Ok(Response::parse_processing_complete(\u0026Response::remove_ack(\n            \u0026response,\n        )))\n    }\n\n    /// Stop the saleae capture\n    pub fn stop_capture(\u0026mut self) -\u003e Result\u003cbool\u003e {\n        self.connection.run_command(\"stop_capture\\0\")?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    //TODO capture_to_file\n    //TODO save_to_file\n    //TODO load_from_file\n\n    /// Close all tabs\n    pub fn close_all_tabs(\u0026mut self) -\u003e Result\u003cbool\u003e {\n        self.connection.run_command(\"close_all_tabs\\0\")?;\n        Ok(self.connection.general_recieve_ack()?)\n    }\n\n    //TODO export_data2\n    //TODO get_analyzers\n    //TODO export_analyzer\n    //TODO is_analyzer_complete\n    //TODO get_capture_range\n    //TODO get_viewstate\n    //TODO get_viewstate\n    //TODO exit\n}\n","traces":[{"line":23,"address":4253312,"length":1,"stats":{"Line":0}},{"line":25,"address":4253326,"length":1,"stats":{"Line":0}},{"line":29,"address":4253392,"length":1,"stats":{"Line":0}},{"line":30,"address":4253407,"length":1,"stats":{"Line":0}},{"line":31,"address":4254006,"length":1,"stats":{"Line":0}},{"line":34,"address":4254512,"length":1,"stats":{"Line":0}},{"line":35,"address":4254527,"length":1,"stats":{"Line":0}},{"line":36,"address":4255117,"length":1,"stats":{"Line":0}},{"line":37,"address":4255160,"length":1,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":4255695,"length":1,"stats":{"Line":0}},{"line":42,"address":4255762,"length":1,"stats":{"Line":0}},{"line":43,"address":4255819,"length":1,"stats":{"Line":0}},{"line":44,"address":4256050,"length":1,"stats":{"Line":0}},{"line":45,"address":4256119,"length":1,"stats":{"Line":0}},{"line":47,"address":4256061,"length":1,"stats":{"Line":0}},{"line":51,"address":4256336,"length":1,"stats":{"Line":0}},{"line":52,"address":4256358,"length":1,"stats":{"Line":0}},{"line":53,"address":4256387,"length":1,"stats":{"Line":0}},{"line":54,"address":4256545,"length":1,"stats":{"Line":0}},{"line":55,"address":4256565,"length":1,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":4233664,"length":1,"stats":{"Line":6}},{"line":73,"address":4233674,"length":1,"stats":{"Line":6}},{"line":82,"address":4233808,"length":1,"stats":{"Line":1}},{"line":83,"address":4233834,"length":1,"stats":{"Line":1}},{"line":84,"address":4233839,"length":1,"stats":{"Line":1}},{"line":85,"address":4234352,"length":1,"stats":{"Line":1}},{"line":88,"address":4234720,"length":1,"stats":{"Line":2}},{"line":89,"address":4234742,"length":1,"stats":{"Line":2}},{"line":90,"address":4234993,"length":1,"stats":{"Line":2}},{"line":91,"address":4235286,"length":1,"stats":{"Line":2}},{"line":92,"address":4235414,"length":1,"stats":{"Line":1}},{"line":93,"address":4235379,"length":1,"stats":{"Line":1}},{"line":96,"address":4235344,"length":1,"stats":{"Line":1}},{"line":101,"address":4235776,"length":1,"stats":{"Line":1}},{"line":102,"address":4235804,"length":1,"stats":{"Line":1}},{"line":103,"address":4235809,"length":1,"stats":{"Line":1}},{"line":104,"address":4236321,"length":1,"stats":{"Line":1}},{"line":111,"address":4236688,"length":1,"stats":{"Line":1}},{"line":112,"address":4236718,"length":1,"stats":{"Line":1}},{"line":113,"address":4236723,"length":1,"stats":{"Line":1}},{"line":114,"address":4236730,"length":1,"stats":{"Line":1}},{"line":116,"address":4237352,"length":1,"stats":{"Line":1}},{"line":119,"address":4237728,"length":1,"stats":{"Line":1}},{"line":120,"address":4237750,"length":1,"stats":{"Line":1}},{"line":121,"address":4237973,"length":1,"stats":{"Line":1}},{"line":122,"address":4238305,"length":1,"stats":{"Line":1}},{"line":123,"address":4238260,"length":1,"stats":{"Line":1}},{"line":127,"address":4238688,"length":1,"stats":{"Line":0}},{"line":128,"address":4238710,"length":1,"stats":{"Line":0}},{"line":129,"address":4238933,"length":1,"stats":{"Line":0}},{"line":130,"address":4239262,"length":1,"stats":{"Line":0}},{"line":131,"address":4239217,"length":1,"stats":{"Line":0}},{"line":136,"address":4239648,"length":1,"stats":{"Line":1}},{"line":137,"address":4239670,"length":1,"stats":{"Line":1}},{"line":138,"address":4239893,"length":1,"stats":{"Line":1}},{"line":139,"address":4240225,"length":1,"stats":{"Line":1}},{"line":140,"address":4240180,"length":1,"stats":{"Line":1}},{"line":145,"address":4240592,"length":1,"stats":{"Line":1}},{"line":146,"address":4240618,"length":1,"stats":{"Line":1}},{"line":147,"address":4240941,"length":1,"stats":{"Line":1}},{"line":148,"address":4241221,"length":1,"stats":{"Line":1}},{"line":154,"address":4241760,"length":1,"stats":{"Line":1}},{"line":155,"address":4241782,"length":1,"stats":{"Line":1}},{"line":156,"address":4242005,"length":1,"stats":{"Line":1}},{"line":157,"address":4242334,"length":1,"stats":{"Line":1}},{"line":158,"address":4242289,"length":1,"stats":{"Line":1}},{"line":169,"address":4242720,"length":1,"stats":{"Line":0}},{"line":170,"address":4242738,"length":1,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":4230688,"length":1,"stats":{"Line":0}},{"line":175,"address":4242973,"length":1,"stats":{"Line":0}},{"line":176,"address":4242981,"length":1,"stats":{"Line":0}},{"line":182,"address":4243792,"length":1,"stats":{"Line":0}},{"line":183,"address":4243814,"length":1,"stats":{"Line":0}},{"line":184,"address":4244037,"length":1,"stats":{"Line":0}},{"line":186,"address":4244321,"length":1,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":4230800,"length":1,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":4244944,"length":1,"stats":{"Line":1}},{"line":196,"address":4244966,"length":1,"stats":{"Line":1}},{"line":197,"address":4245205,"length":1,"stats":{"Line":1}},{"line":198,"address":4245537,"length":1,"stats":{"Line":1}},{"line":199,"address":4245492,"length":1,"stats":{"Line":1}},{"line":207,"address":4246448,"length":1,"stats":{"Line":1}},{"line":212,"address":4246483,"length":1,"stats":{"Line":1}},{"line":213,"address":4246524,"length":1,"stats":{"Line":1}},{"line":214,"address":4246504,"length":1,"stats":{"Line":1}},{"line":215,"address":4246514,"length":1,"stats":{"Line":1}},{"line":217,"address":4247085,"length":1,"stats":{"Line":1}},{"line":221,"address":4247760,"length":1,"stats":{"Line":1}},{"line":222,"address":4247782,"length":1,"stats":{"Line":1}},{"line":223,"address":4248002,"length":1,"stats":{"Line":1}},{"line":230,"address":4248288,"length":1,"stats":{"Line":1}},{"line":231,"address":4248307,"length":1,"stats":{"Line":1}},{"line":237,"address":4248576,"length":1,"stats":{"Line":1}},{"line":238,"address":4248591,"length":1,"stats":{"Line":1}},{"line":239,"address":4248794,"length":1,"stats":{"Line":1}},{"line":243,"address":4249088,"length":1,"stats":{"Line":1}},{"line":244,"address":4249110,"length":1,"stats":{"Line":1}},{"line":245,"address":4249333,"length":1,"stats":{"Line":1}},{"line":246,"address":4249665,"length":1,"stats":{"Line":1}},{"line":247,"address":4249620,"length":1,"stats":{"Line":1}},{"line":252,"address":4250032,"length":1,"stats":{"Line":1}},{"line":253,"address":4250054,"length":1,"stats":{"Line":1}},{"line":254,"address":4250274,"length":1,"stats":{"Line":1}},{"line":262,"address":4250560,"length":1,"stats":{"Line":1}},{"line":263,"address":4250582,"length":1,"stats":{"Line":1}},{"line":264,"address":4250802,"length":1,"stats":{"Line":1}}],"covered":71,"coverable":113},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","device.rs"],"content":"//! This module defines devices that are connected to the saleae logic program.\n//!\n//! ## Example of response from Logic\n//! ```text\n//! 1, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0xdf03c43d1f3aa2f3, ACTIVE\n//! ```\n//! ## C# struct\n//! ```text\n//! struct ConnectedDevices\n//! {\n//!   String type;\n//!   String name;\n//!   int device_id;\n//!   int index;\n//!   bool is_active;\n//! }\n//! ```\n\nuse anyhow::Result;\nuse std::str::FromStr;\n\ncustom_derive! {\n    /// Device id for saleae devices\n    #[allow(non_camel_case_types)]\n    #[derive(Debug, EnumFromStr, PartialEq)]\n    pub enum DeviceID {\n        /// Regular 4 Wire device\n        LOGIC_4_DEVICE,\n        /// Regular 8 Wire device\n        LOGIC_8_DEVICE,\n        /// Pro 8 Wire device\n        LOGIC_PRO_8_DEVICE,\n        /// Pro 16 Wire device\n        LOGIC_PRO_16_DEVICE,\n    }\n}\n\n/// Connected Device to saleae, the main usability comes from the fromStr trait.\n#[derive(Debug, PartialEq)]\npub struct ConnectedDevice {\n    /// type of device\n    pub d_type: String,\n    /// name of device\n    pub name: String,\n    /// id of device\n    pub device_id: DeviceID,\n    /// index of device\n    pub index: String,\n    /// if device is active\n    pub is_active: bool,\n}\n\nimpl FromStr for ConnectedDevice {\n    type Err = std::num::ParseIntError;\n    fn from_str(response: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n        let v: Vec\u003c\u0026str\u003e = response.split(',').map(|a| a.trim_start()).collect();\n\n        /* parse into device_id */\n        let device_id: DeviceID = v[2].parse().unwrap();\n\n        /*\n         * last element is ACTIVE, if that element doesn't don't cause a panic by\n         * checking that element\n         */\n        let is_active = v.len() == 5 \u0026\u0026 v[4] == \"ACTIVE\";\n\n        Ok(ConnectedDevice {\n            d_type: v[0].to_string(),\n            name: v[1].to_string(),\n            device_id,\n            index: v[3].to_string(),\n            is_active,\n        })\n    }\n}\n","traces":[{"line":55,"address":4227568,"length":1,"stats":{"Line":3}},{"line":56,"address":4227588,"length":1,"stats":{"Line":6}},{"line":59,"address":4227696,"length":1,"stats":{"Line":3}},{"line":65,"address":4227784,"length":1,"stats":{"Line":3}},{"line":67,"address":4228081,"length":1,"stats":{"Line":3}},{"line":68,"address":4227847,"length":1,"stats":{"Line":3}},{"line":69,"address":4227942,"length":1,"stats":{"Line":3}},{"line":70,"address":4227999,"length":1,"stats":{"Line":3}},{"line":71,"address":4228020,"length":1,"stats":{"Line":3}},{"line":72,"address":4228074,"length":1,"stats":{"Line":3}}],"covered":10,"coverable":10},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","request.rs"],"content":"//! This module helps create requests to the Saleae Logic software\n//!\n//! The function here parse and type check the input from the API into strings to send into\n//! the Saleae socket\nuse anyhow::Result;\n\npub struct Request {}\n\nimpl Request {\n    pub fn prepare_set_active_channels(\n        digital_channels: \u0026[u8],\n        analog_channels: \u0026[u8],\n    ) -\u003e Result\u003cString\u003e {\n        // Check only one kind of empty\n        if digital_channels.is_empty() \u0026\u0026 analog_channels.is_empty() {\n            return Err(anyhow!(\n                \"Logic requires at least one active channel, no active channels found\"\n            ));\n        }\n\n        let d_str = if !digital_channels.is_empty() {\n            format!(\n                \", digital_channels, {}\",\n                Request::create_channel_str(digital_channels)?\n            )\n        } else {\n            \"\".to_string()\n        };\n\n        let a_str = if !analog_channels.is_empty() {\n            format!(\n                \", analog_channels, {}\",\n                Request::create_channel_str(analog_channels)?\n            )\n        } else {\n            \"\".to_string()\n        };\n\n        Ok(format!(\"set_active_channels{}{}\\0\", d_str, a_str))\n    }\n}\n\n/// Helper functions\nimpl Request {\n    pub fn create_channel_str(v: \u0026[u8]) -\u003e Result\u003cString\u003e {\n        let s = v\n            .iter()\n            .map(|a| format!(\"{}, \", a.to_string()))\n            .collect::\u003cString\u003e();\n        Ok(s[..s.len() - 2].to_string())\n    }\n}\n","traces":[{"line":10,"address":4286880,"length":1,"stats":{"Line":2}},{"line":15,"address":4286922,"length":1,"stats":{"Line":2}},{"line":16,"address":4287143,"length":1,"stats":{"Line":1}},{"line":17,"address":null,"length":0,"stats":{"Line":0}},{"line":21,"address":4287106,"length":1,"stats":{"Line":2}},{"line":22,"address":4287351,"length":1,"stats":{"Line":0}},{"line":23,"address":4287253,"length":1,"stats":{"Line":2}},{"line":24,"address":4287260,"length":1,"stats":{"Line":2}},{"line":27,"address":4287228,"length":1,"stats":{"Line":1}},{"line":30,"address":4287758,"length":1,"stats":{"Line":2}},{"line":31,"address":4287946,"length":1,"stats":{"Line":0}},{"line":32,"address":4287844,"length":1,"stats":{"Line":2}},{"line":33,"address":4287851,"length":1,"stats":{"Line":2}},{"line":36,"address":4287815,"length":1,"stats":{"Line":1}},{"line":39,"address":4288349,"length":1,"stats":{"Line":2}},{"line":45,"address":4289584,"length":1,"stats":{"Line":2}},{"line":46,"address":4289604,"length":1,"stats":{"Line":2}},{"line":47,"address":null,"length":0,"stats":{"Line":0}},{"line":48,"address":4232032,"length":1,"stats":{"Line":2}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":4289710,"length":1,"stats":{"Line":2}}],"covered":16,"coverable":21},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","response.rs"],"content":"//! This module help discern and parse the responses from saleae\n\nuse crate::device::ConnectedDevice;\nuse crate::samplerate::SampleRate;\nuse anyhow::Result;\nuse std::str::FromStr;\n\n/// struct to handle responses\npub struct Response {}\n\n//TODO add errors\nimpl Response {\n    /// Return string without \"\\nACK\" string line nor extra 0 char's from buffer\n    pub fn remove_ack(response: \u0026str) -\u003e String {\n        response\n            .trim_end_matches(char::from(0))\n            .trim_end_matches(\"\\nACK\")\n            .to_string()\n    }\n\n    /// Check if last string is ACK\n    pub fn verify_ack(response: \u0026str) -\u003e bool {\n        response.lines().last().unwrap() == \"ACK\"\n    }\n\n    /// Parse the performance response\n    ///\n    /// # Sample Input\n    /// The following values are expected as input:\n    /// ```text, no_run\n    /// 100\n    /// 80\n    /// 60\n    /// 40\n    /// 20\n    /// ```\n    pub fn parse_performance(response: \u0026str) -\u003e u8 {\n        response.parse::\u003cu8\u003e().unwrap()\n    }\n\n    pub fn parse_num_samples(response: \u0026str) -\u003e u32 {\n        response.parse::\u003cu32\u003e().unwrap()\n    }\n\n    ///\n    pub fn parse_get_sample_rate(response: \u0026str) -\u003e SampleRate {\n        let mut iter = response.lines();\n        SampleRate {\n            DigitalSampleRate: iter.next().unwrap().parse::\u003cu32\u003e().unwrap(),\n            AnalogSampleRate: iter.next().unwrap().parse::\u003cu32\u003e().unwrap(),\n        }\n    }\n\n    /// Parse get all sample rates\n    ///\n    /// # Sample input\n    /// ```text\n    /// 5000000, 1250000\n    /// 10000000, 625000\n    /// ```\n    pub fn parse_get_all_sample_rates(response: \u0026str) -\u003e Vec\u003cSampleRate\u003e {\n        response\n            .lines()\n            .map(|a| SampleRate::from_str(\u0026a).unwrap())\n            .collect()\n    }\n\n    /// Parse the connected_devices reponse into ConnectedDevice\n    ///\n    /// # Sample Input\n    /// ```text\n    /// 1, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0xdf03c43d1f3aa2f3, ACTIVE\n    /// ```\n    pub fn parse_connected_devices(response: \u0026str) -\u003e Vec\u003cConnectedDevice\u003e {\n        response\n            .lines()\n            .map(|a| ConnectedDevice::from_str(\u0026a).unwrap())\n            .collect()\n    }\n\n    pub fn parse_get_active_channels(response: \u0026str) -\u003e Result\u003cVec\u003cVec\u003cu8\u003e\u003e\u003e {\n        println!(\"{}\", response);\n        let v: Vec\u003c\u0026str\u003e = response.split(',').map(|a| a.trim_start()).collect();\n\n        // Find position of starter word\n        let digital_pos = v.iter().position(|a| *a == \"digital_channels\").unwrap();\n        let analog_pos = v.iter().position(|a| *a == \"analog_channels\").unwrap();\n\n        // Parse in between words to find values\n        let digital_res: Vec\u003cu8\u003e = v[digital_pos + 1..analog_pos]\n            .iter()\n            .map(|a| a.parse().unwrap())\n            .collect();\n\n        let analog_res: Vec\u003cu8\u003e = v[analog_pos + 1..v.len()]\n            .iter()\n            .map(|a| a.parse().unwrap())\n            .collect();\n        Ok(vec![digital_res, analog_res])\n    }\n\n    /// Parse if processing is complete\n    ///\n    /// # Sample Input\n    /// ```text\n    /// FALSE\n    /// ```\n    /// ```text\n    /// TRUE\n    /// ```\n    pub fn parse_processing_complete(response: \u0026str) -\u003e bool {\n        response == \"TRUE\"\n    }\n}\n","traces":[{"line":14,"address":4279104,"length":1,"stats":{"Line":5}},{"line":15,"address":4279123,"length":1,"stats":{"Line":5}},{"line":22,"address":4279264,"length":1,"stats":{"Line":3}},{"line":23,"address":4279281,"length":1,"stats":{"Line":3}},{"line":37,"address":4279392,"length":1,"stats":{"Line":2}},{"line":38,"address":4279406,"length":1,"stats":{"Line":2}},{"line":41,"address":4279488,"length":1,"stats":{"Line":2}},{"line":42,"address":4279502,"length":1,"stats":{"Line":2}},{"line":46,"address":4279568,"length":1,"stats":{"Line":1}},{"line":47,"address":4279585,"length":1,"stats":{"Line":1}},{"line":49,"address":4279633,"length":1,"stats":{"Line":1}},{"line":50,"address":4279784,"length":1,"stats":{"Line":1}},{"line":61,"address":4279984,"length":1,"stats":{"Line":1}},{"line":62,"address":4280004,"length":1,"stats":{"Line":1}},{"line":74,"address":4280080,"length":1,"stats":{"Line":2}},{"line":75,"address":4280100,"length":1,"stats":{"Line":2}},{"line":81,"address":4280176,"length":1,"stats":{"Line":2}},{"line":82,"address":4280202,"length":1,"stats":{"Line":2}},{"line":83,"address":4280435,"length":1,"stats":{"Line":4}},{"line":86,"address":4280520,"length":1,"stats":{"Line":4}},{"line":87,"address":4280729,"length":1,"stats":{"Line":4}},{"line":90,"address":4280912,"length":1,"stats":{"Line":2}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":4336736,"length":1,"stats":{"Line":2}},{"line":93,"address":4281093,"length":1,"stats":{"Line":2}},{"line":95,"address":4281101,"length":1,"stats":{"Line":2}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":4336832,"length":1,"stats":{"Line":2}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":4281308,"length":1,"stats":{"Line":2}},{"line":111,"address":4281696,"length":1,"stats":{"Line":2}},{"line":112,"address":4281710,"length":1,"stats":{"Line":2}}],"covered":29,"coverable":32},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","samplerate.rs"],"content":"//! This module defines the sample rates returned and used by the saleae Logic program\n//!\n//! # C# Example\n//! ```text\n//! struct SampleRate\n//! {\n//!     public int AnalogSampleRate;\n//!     public int DigitalSampleRate;\n//! }\n//! ```\nuse std::str::FromStr;\n\n#[allow(non_snake_case)]\n#[derive(Debug, PartialEq)]\npub struct SampleRate {\n    pub DigitalSampleRate: u32,\n    pub AnalogSampleRate: u32,\n}\n\nimpl FromStr for SampleRate {\n    type Err = std::num::ParseIntError;\n    fn from_str(response: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n        let v: Vec\u003c\u0026str\u003e = response.split(',').map(|a| a.trim_start()).collect();\n\n        Ok(SampleRate {\n            DigitalSampleRate: v[0].parse::\u003cu32\u003e().unwrap(),\n            AnalogSampleRate: v[1].parse::\u003cu32\u003e().unwrap(),\n        })\n    }\n}\n","traces":[{"line":22,"address":4211904,"length":1,"stats":{"Line":2}},{"line":23,"address":4211924,"length":1,"stats":{"Line":4}},{"line":25,"address":4212233,"length":1,"stats":{"Line":2}},{"line":26,"address":4212025,"length":1,"stats":{"Line":2}},{"line":27,"address":4212138,"length":1,"stats":{"Line":2}}],"covered":5,"coverable":5},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","tests","client.rs"],"content":"#[cfg(test)]\nmod tests {\n    use saleae::client::{Client, Connection};\n    use saleae::device::DeviceID;\n    use saleae::ConnectedDevice;\n    use saleae::PerformanceOption;\n    use saleae::SampleRate;\n\n    #[test]\n    fn set_num_samples() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.set_num_samples(500).unwrap();\n        assert_eq!(true, response);\n    }\n\n    #[test]\n    fn get_num_samples() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"3000\\nACK\".to_string())) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.get_num_samples().unwrap();\n        assert_eq!(3000, response);\n    }\n\n    #[test]\n    #[should_panic]\n    fn get_num_samples_fail() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"3000\".to_string())) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.get_num_samples().unwrap();\n        assert_eq!(3000, response);\n    }\n\n    #[test]\n    fn set_capture_seconds() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.set_capture_seconds(2.2).unwrap();\n        assert_eq!(true, response);\n    }\n\n    #[test]\n    fn set_sample_rate() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn\n            .set_sample_rate(\u0026SampleRate {\n                AnalogSampleRate: 6250000,\n                DigitalSampleRate: 1562500,\n            })\n            .unwrap();\n        assert_eq!(true, response);\n    }\n\n    #[test]\n    fn get_sample_rate() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe {\n            faux::when!(conn.general_recieve_message).then(|_| Ok(\"1000000\\n0\\n\".to_string()))\n        }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.get_sample_rate().unwrap();\n        assert_eq!(response.DigitalSampleRate, 1000000);\n        assert_eq!(response.AnalogSampleRate, 0);\n    }\n\n    #[test]\n    fn get_performance() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"100\".to_string())) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.get_performance().unwrap();\n        assert_eq!(response, 100);\n    }\n\n    #[test]\n    fn set_performance() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.set_performance(PerformanceOption::Full).unwrap();\n        assert_eq!(response, true);\n\n        let mut conn2 = Connection::faux();\n        unsafe { faux::when!(conn2.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn2.general_recieve_ack).then(|_| Ok(false)) }\n\n        let mut conn2 = Client::new(conn2).unwrap();\n        let response2 = conn2.set_performance(PerformanceOption::Low).unwrap();\n        assert_eq!(response2, false);\n    }\n\n    #[test]\n    fn get_connected_devices() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe {\n            faux::when!(conn.general_recieve_message).then(|_| Ok(\"1, Logic 8, LOGIC_8_DEVICE, 0x2dc9, ACTIVE\\n2, Logic Pro 8, LOGIC_PRO_8_DEVICE, 0x7243\\n3, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0x673f\\n4, Logic 4, LOGIC_4_DEVICE, 0x6709\\nACK\".to_string()))\n        }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.get_connected_devices().unwrap();\n        assert_eq!(\n            response[0],\n            ConnectedDevice {\n                d_type: \"1\".to_string(),\n                name: \"Logic 8\".to_string(),\n                device_id: DeviceID::LOGIC_8_DEVICE,\n                index: \"0x2dc9\".to_string(),\n                is_active: true\n            }\n        );\n        assert_eq!(\n            response[1],\n            ConnectedDevice {\n                d_type: \"2\".to_string(),\n                name: \"Logic Pro 8\".to_string(),\n                device_id: DeviceID::LOGIC_PRO_8_DEVICE,\n                index: \"0x7243\".to_string(),\n                is_active: false\n            }\n        );\n        assert_eq!(\n            response[2],\n            ConnectedDevice {\n                d_type: \"3\".to_string(),\n                name: \"Logic Pro 16\".to_string(),\n                device_id: DeviceID::LOGIC_PRO_16_DEVICE,\n                index: \"0x673f\".to_string(),\n                is_active: false\n            }\n        );\n        assert_eq!(\n            response[3],\n            ConnectedDevice {\n                d_type: \"4\".to_string(),\n                name: \"Logic 4\".to_string(),\n                device_id: DeviceID::LOGIC_4_DEVICE,\n                index: \"0x6709\".to_string(),\n                is_active: false\n            }\n        );\n    }\n\n    #[test]\n    fn select_active_device() {\n        //TODO can't test b/c of get_connected_devices not being fauxable\n        //let device = ConnectedDevice {\n        //    d_type: \"4\".to_string(),\n        //    name: \"Logic 4\".to_string(),\n        //    device_id: DeviceID::LOGIC_4_DEVICE,\n        //    index: \"0x6709\".to_string(),\n        //    is_active: false,\n        //};\n\n        //let mut conn = Connection::faux();\n        //unsafe { faux::when!(conn.get_connected_devices).then(|_| Ok(device)) };\n        //unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n\n        //let mut conn = Client::new(conn).unwrap();\n        //let response = conn.select_active_device(device).unwrap();\n        //assert_eq!(response, true);\n    }\n\n    #[test]\n    fn get_active_device() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe {\n            faux::when!(conn.general_recieve_message).then(|_| {\n                Ok(\"digital_channels, 0, 4, 5, 7, analog_channels, 0, 1, 2, 5, 8\".to_string())\n            })\n        }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.get_active_channels().unwrap();\n        assert_eq!(response[0], [0, 4, 5, 7]);\n        assert_eq!(response[1], [0, 1, 2, 5, 8]);\n    }\n\n    #[test]\n    fn set_active_channels() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn\n            .set_active_channels(\u0026[0, 4, 5, 7], \u0026[0, 1, 2, 5, 8])\n            .unwrap();\n        assert_eq!(response, true);\n    }\n\n    #[test]\n    fn reset_active_channels() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.reset_active_channels().unwrap();\n        assert_eq!(response, true);\n    }\n\n    #[test]\n    fn start_capture() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.start_capture().unwrap();\n        assert_eq!(response, true);\n    }\n\n    #[test]\n    fn start_capture_block_until_finished() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.start_capture_block_until_finished().unwrap();\n        assert_eq!(response, true);\n    }\n\n    #[test]\n    fn is_processing_complete() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"TRUE\\nACK\".to_string())) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.is_processing_complete().unwrap();\n        assert_eq!(response, true);\n    }\n\n    #[test]\n    fn stop_capture() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.stop_capture().unwrap();\n        assert_eq!(response, true);\n    }\n\n    #[test]\n    fn close_all_tabs() {\n        let mut conn = Connection::faux();\n        unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n        unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n        let mut conn = Client::new(conn).unwrap();\n        let response = conn.close_all_tabs().unwrap();\n        assert_eq!(response, true);\n    }\n}\n","traces":[{"line":10,"address":4240032,"length":1,"stats":{"Line":2}},{"line":11,"address":4240039,"length":1,"stats":{"Line":1}},{"line":12,"address":4240083,"length":1,"stats":{"Line":2}},{"line":13,"address":4240122,"length":1,"stats":{"Line":2}},{"line":15,"address":4240161,"length":1,"stats":{"Line":1}},{"line":16,"address":4240294,"length":1,"stats":{"Line":1}},{"line":17,"address":4240383,"length":1,"stats":{"Line":1}},{"line":21,"address":4240912,"length":1,"stats":{"Line":2}},{"line":22,"address":4240919,"length":1,"stats":{"Line":1}},{"line":23,"address":4240963,"length":1,"stats":{"Line":2}},{"line":24,"address":4241002,"length":1,"stats":{"Line":2}},{"line":26,"address":4241041,"length":1,"stats":{"Line":1}},{"line":27,"address":4241174,"length":1,"stats":{"Line":1}},{"line":28,"address":4241256,"length":1,"stats":{"Line":1}},{"line":33,"address":4241776,"length":1,"stats":{"Line":2}},{"line":34,"address":4241783,"length":1,"stats":{"Line":1}},{"line":35,"address":4241827,"length":1,"stats":{"Line":2}},{"line":36,"address":4241866,"length":1,"stats":{"Line":2}},{"line":38,"address":4241905,"length":1,"stats":{"Line":1}},{"line":39,"address":4242038,"length":1,"stats":{"Line":1}},{"line":40,"address":4242120,"length":1,"stats":{"Line":0}},{"line":44,"address":4242640,"length":1,"stats":{"Line":2}},{"line":45,"address":4242647,"length":1,"stats":{"Line":1}},{"line":46,"address":4242691,"length":1,"stats":{"Line":2}},{"line":47,"address":4242730,"length":1,"stats":{"Line":2}},{"line":49,"address":4242769,"length":1,"stats":{"Line":1}},{"line":50,"address":4242902,"length":1,"stats":{"Line":1}},{"line":51,"address":4242994,"length":1,"stats":{"Line":1}},{"line":55,"address":4243520,"length":1,"stats":{"Line":2}},{"line":56,"address":4243527,"length":1,"stats":{"Line":1}},{"line":57,"address":4243571,"length":1,"stats":{"Line":2}},{"line":58,"address":4243610,"length":1,"stats":{"Line":2}},{"line":60,"address":4243649,"length":1,"stats":{"Line":1}},{"line":61,"address":4243789,"length":1,"stats":{"Line":1}},{"line":62,"address":4243782,"length":1,"stats":{"Line":1}},{"line":67,"address":4243873,"length":1,"stats":{"Line":1}},{"line":71,"address":4244400,"length":1,"stats":{"Line":2}},{"line":72,"address":4244407,"length":1,"stats":{"Line":1}},{"line":73,"address":4244451,"length":1,"stats":{"Line":2}},{"line":75,"address":4244490,"length":1,"stats":{"Line":2}},{"line":78,"address":4244529,"length":1,"stats":{"Line":1}},{"line":79,"address":4244671,"length":1,"stats":{"Line":1}},{"line":80,"address":4244768,"length":1,"stats":{"Line":1}},{"line":81,"address":4244865,"length":1,"stats":{"Line":1}},{"line":85,"address":4245728,"length":1,"stats":{"Line":2}},{"line":86,"address":4245735,"length":1,"stats":{"Line":1}},{"line":87,"address":4245779,"length":1,"stats":{"Line":2}},{"line":88,"address":4245818,"length":1,"stats":{"Line":2}},{"line":90,"address":4245857,"length":1,"stats":{"Line":1}},{"line":91,"address":4245990,"length":1,"stats":{"Line":1}},{"line":92,"address":4246072,"length":1,"stats":{"Line":1}},{"line":96,"address":4246608,"length":1,"stats":{"Line":2}},{"line":97,"address":4246615,"length":1,"stats":{"Line":1}},{"line":98,"address":4246670,"length":1,"stats":{"Line":2}},{"line":99,"address":4246712,"length":1,"stats":{"Line":2}},{"line":101,"address":4246754,"length":1,"stats":{"Line":1}},{"line":102,"address":4246899,"length":1,"stats":{"Line":1}},{"line":103,"address":4247005,"length":1,"stats":{"Line":1}},{"line":105,"address":4247100,"length":1,"stats":{"Line":1}},{"line":106,"address":4247437,"length":1,"stats":{"Line":2}},{"line":107,"address":4247487,"length":1,"stats":{"Line":2}},{"line":109,"address":4247529,"length":1,"stats":{"Line":1}},{"line":110,"address":4247674,"length":1,"stats":{"Line":1}},{"line":111,"address":4247774,"length":1,"stats":{"Line":1}},{"line":115,"address":4248400,"length":1,"stats":{"Line":2}},{"line":116,"address":4248407,"length":1,"stats":{"Line":1}},{"line":117,"address":4248454,"length":1,"stats":{"Line":2}},{"line":119,"address":4248496,"length":1,"stats":{"Line":2}},{"line":122,"address":4248538,"length":1,"stats":{"Line":1}},{"line":123,"address":4248683,"length":1,"stats":{"Line":1}},{"line":124,"address":4249034,"length":1,"stats":{"Line":1}},{"line":125,"address":4248774,"length":1,"stats":{"Line":1}},{"line":126,"address":4248908,"length":1,"stats":{"Line":1}},{"line":127,"address":4248789,"length":1,"stats":{"Line":1}},{"line":128,"address":4248831,"length":1,"stats":{"Line":1}},{"line":129,"address":4248858,"length":1,"stats":{"Line":1}},{"line":130,"address":4248866,"length":1,"stats":{"Line":1}},{"line":134,"address":4249805,"length":1,"stats":{"Line":1}},{"line":135,"address":4249557,"length":1,"stats":{"Line":1}},{"line":136,"address":4249679,"length":1,"stats":{"Line":1}},{"line":137,"address":4249572,"length":1,"stats":{"Line":1}},{"line":138,"address":4249599,"length":1,"stats":{"Line":1}},{"line":139,"address":4249626,"length":1,"stats":{"Line":1}},{"line":140,"address":4249634,"length":1,"stats":{"Line":1}},{"line":144,"address":4250576,"length":1,"stats":{"Line":1}},{"line":145,"address":4250328,"length":1,"stats":{"Line":1}},{"line":146,"address":4250450,"length":1,"stats":{"Line":1}},{"line":147,"address":4250343,"length":1,"stats":{"Line":1}},{"line":148,"address":4250370,"length":1,"stats":{"Line":1}},{"line":149,"address":4250397,"length":1,"stats":{"Line":1}},{"line":150,"address":4250405,"length":1,"stats":{"Line":1}},{"line":154,"address":4251305,"length":1,"stats":{"Line":1}},{"line":155,"address":4251063,"length":1,"stats":{"Line":1}},{"line":156,"address":4251182,"length":1,"stats":{"Line":1}},{"line":157,"address":4251075,"length":1,"stats":{"Line":1}},{"line":158,"address":4251102,"length":1,"stats":{"Line":1}},{"line":159,"address":4251129,"length":1,"stats":{"Line":1}},{"line":160,"address":4251137,"length":1,"stats":{"Line":1}},{"line":167,"address":4252160,"length":1,"stats":{"Line":2}},{"line":187,"address":4252176,"length":1,"stats":{"Line":2}},{"line":188,"address":4252183,"length":1,"stats":{"Line":1}},{"line":189,"address":4252230,"length":1,"stats":{"Line":2}},{"line":191,"address":4252272,"length":1,"stats":{"Line":2}},{"line":192,"address":4265934,"length":1,"stats":{"Line":1}},{"line":196,"address":4252314,"length":1,"stats":{"Line":1}},{"line":197,"address":4252459,"length":1,"stats":{"Line":1}},{"line":198,"address":4252546,"length":1,"stats":{"Line":1}},{"line":199,"address":4252698,"length":1,"stats":{"Line":1}},{"line":203,"address":4253600,"length":1,"stats":{"Line":2}},{"line":204,"address":4253607,"length":1,"stats":{"Line":1}},{"line":205,"address":4253651,"length":1,"stats":{"Line":2}},{"line":206,"address":4253690,"length":1,"stats":{"Line":2}},{"line":208,"address":4253729,"length":1,"stats":{"Line":1}},{"line":209,"address":4253876,"length":1,"stats":{"Line":1}},{"line":210,"address":4253862,"length":1,"stats":{"Line":1}},{"line":212,"address":4253971,"length":1,"stats":{"Line":1}},{"line":216,"address":4254496,"length":1,"stats":{"Line":2}},{"line":217,"address