Bug Fixes
This commit is contained in:
parent
ba1b0ddc22
commit
e69c549750
8 changed files with 1143 additions and 175 deletions
283
src/component.rs
283
src/component.rs
|
|
@ -1,72 +1,255 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::sync::{Arc, Mutex, atomic::{AtomicBool, Ordering}};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use crate::pin::{Pin, PinValue};
|
||||
// Component trait
|
||||
pub trait Component: Send {
|
||||
fn name(&self) -> &str;
|
||||
fn pins(&self) -> &HashMap<String, Arc<Pin>>;
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>>;
|
||||
fn update(&mut self) -> Result<(), String>;
|
||||
|
||||
use crate::pin::Pin;
|
||||
|
||||
pub trait Component {
|
||||
fn name(&self) -> String;
|
||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>>;
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String>;
|
||||
fn update(&mut self);
|
||||
fn run(&mut self);
|
||||
fn stop(&mut self);
|
||||
|
||||
// Helper method to check if component is running
|
||||
fn is_running(&self) -> bool;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
|
||||
pub struct BaseComponent {
|
||||
pub name: String,
|
||||
pub pins: HashMap<String, Arc<Pin>>,
|
||||
pub running: Arc<Mutex<bool>>,
|
||||
name: String,
|
||||
pins: HashMap<String, Arc<Mutex<Pin>>>,
|
||||
running: AtomicBool,
|
||||
}
|
||||
|
||||
impl BaseComponent {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
pub fn new(name: String, pins: HashMap<String, Arc<Mutex<Pin>>>) -> Self {
|
||||
BaseComponent {
|
||||
name,
|
||||
pins: HashMap::new(),
|
||||
running: Arc::new(Mutex::new(false)),
|
||||
pins,
|
||||
running: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
pub fn add_pin(&mut self, name: String, initial_value: PinValue, initial_output_enable: bool) -> Arc<Pin> {
|
||||
let pin = Arc::new(Pin {
|
||||
name: name.clone(),
|
||||
value: Arc::new(RwLock::new(initial_value)),
|
||||
output_enable: Arc::new(Mutex::new(initial_output_enable)),
|
||||
connections: Arc::new(Mutex::new(Vec::new())),
|
||||
});
|
||||
|
||||
self.pins.insert(name, pin.clone());
|
||||
pin
|
||||
}
|
||||
}
|
||||
impl Component for BaseComponent {
|
||||
fn name(&self) -> &str {
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.running.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn set_running(&self, running: bool) {
|
||||
self.running.store(running, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn create_pin_map(pin_names: &[&str], component_name: &str) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||
let mut pins = HashMap::new();
|
||||
for pin_name in pin_names {
|
||||
pins.insert(
|
||||
pin_name.to_string(),
|
||||
Arc::new(Mutex::new(Pin::new(format!("{}_{}", component_name, pin_name))))
|
||||
);
|
||||
}
|
||||
pins
|
||||
}
|
||||
|
||||
pub fn add_pin(&mut self, name: String, pin: Arc<Mutex<Pin>>) {
|
||||
self.pins.insert(name, pin);
|
||||
}
|
||||
|
||||
pub fn remove_pin(&mut self, name: &str) -> Option<Arc<Mutex<Pin>>> {
|
||||
self.pins.remove(name)
|
||||
}
|
||||
|
||||
// Helper method to get a mutable reference to pins (for components that need to modify pins)
|
||||
pub fn pins_mut(&mut self) -> &mut HashMap<String, Arc<Mutex<Pin>>> {
|
||||
&mut self.pins
|
||||
}
|
||||
}
|
||||
|
||||
// Implement Clone manually without AtomicBool
|
||||
impl Clone for BaseComponent {
|
||||
fn clone(&self) -> Self {
|
||||
// Create new pins with the same names but new Pin instances
|
||||
let mut new_pins = HashMap::new();
|
||||
for (name, pin) in &self.pins {
|
||||
let pin_guard = pin.lock().unwrap();
|
||||
new_pins.insert(
|
||||
name.clone(),
|
||||
Arc::new(Mutex::new(Pin::new(pin_guard.name().to_string())))
|
||||
);
|
||||
}
|
||||
|
||||
BaseComponent {
|
||||
name: self.name.clone(),
|
||||
pins: new_pins,
|
||||
running: AtomicBool::new(false), // Always start as not running when cloned
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default implementation for Component trait
|
||||
impl Component for BaseComponent {
|
||||
fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
||||
&self.pins
|
||||
}
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||
self.pins.get(name).cloned()
|
||||
}
|
||||
fn update(&mut self) -> Result<(), String> {
|
||||
// Base implementation does nothing
|
||||
Ok(())
|
||||
}
|
||||
fn run(&mut self) {
|
||||
*self.running.lock().unwrap() = true;
|
||||
let running = self.running.clone();
|
||||
let mut self_clone = self.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
while *running.lock().unwrap() {
|
||||
if let Err(e) = self_clone.update() {
|
||||
eprintln!("Error in {}: {}", self_clone.name, e);
|
||||
}
|
||||
thread::sleep(Duration::from_micros(10));
|
||||
}
|
||||
});
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
||||
self.pins.get(name)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("Pin {} not found on component {}", name, self.name))
|
||||
}
|
||||
|
||||
fn update(&mut self) {
|
||||
// Base implementation does nothing - components should override this
|
||||
// This is where pin state changes would be processed
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
self.set_running(true);
|
||||
|
||||
// Simple run loop - components should override this for their specific behavior
|
||||
while self.is_running() {
|
||||
self.update();
|
||||
thread::sleep(Duration::from_micros(10)); // Prevent busy waiting
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
*self.running.lock().unwrap() = false;
|
||||
self.set_running(false);
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.running.load(Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create component pins easily
|
||||
pub fn create_component_pins(component_name: &str, pin_definitions: &[(&str, &str)]) -> HashMap<String, Arc<Mutex<Pin>>> {
|
||||
let mut pins = HashMap::new();
|
||||
|
||||
for (pin_name, pin_type) in pin_definitions {
|
||||
let full_pin_name = format!("{}_{}", component_name, pin_name);
|
||||
let mut pin = Pin::new(full_pin_name);
|
||||
|
||||
// Set initial state based on pin type
|
||||
match *pin_type {
|
||||
"input" | "in" => {
|
||||
// Input pins start as HighZ (waiting for input)
|
||||
pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::HighZ);
|
||||
}
|
||||
"output" | "out" => {
|
||||
// Output pins start as HighZ (tri-state)
|
||||
pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::HighZ);
|
||||
}
|
||||
"power" => {
|
||||
// Power pins start high
|
||||
pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::High);
|
||||
}
|
||||
"ground" => {
|
||||
// Ground pins start low
|
||||
pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::Low);
|
||||
}
|
||||
_ => {
|
||||
// Default to HighZ
|
||||
pin.set_driver(Some("internal".to_string()), crate::pin::PinValue::HighZ);
|
||||
}
|
||||
}
|
||||
|
||||
pins.insert(pin_name.to_string(), Arc::new(Mutex::new(pin)));
|
||||
}
|
||||
|
||||
pins
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_base_component_creation() {
|
||||
let pins = BaseComponent::create_pin_map(&["A0", "A1", "D0", "D1"], "TEST");
|
||||
let component = BaseComponent::new("TEST_COMP".to_string(), pins);
|
||||
|
||||
assert_eq!(component.name(), "TEST_COMP");
|
||||
assert!(!component.is_running());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base_component_pin_management() {
|
||||
let pins = BaseComponent::create_pin_map(&["A0", "D0"], "TEST");
|
||||
let mut component = BaseComponent::new("TEST_COMP".to_string(), pins);
|
||||
|
||||
// Test getting existing pin
|
||||
assert!(component.get_pin("A0").is_ok());
|
||||
|
||||
// Test getting non-existent pin
|
||||
assert!(component.get_pin("NON_EXISTENT").is_err());
|
||||
|
||||
// Test adding new pin
|
||||
let new_pin = Arc::new(Mutex::new(Pin::new("TEST_NEW".to_string())));
|
||||
component.add_pin("NEW_PIN".to_string(), new_pin);
|
||||
assert!(component.get_pin("NEW_PIN").is_ok());
|
||||
|
||||
// Test removing pin
|
||||
let removed = component.remove_pin("A0");
|
||||
assert!(removed.is_some());
|
||||
assert!(component.get_pin("A0").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base_component_lifecycle() {
|
||||
let pins = BaseComponent::create_pin_map(&["TEST"], "TEST");
|
||||
let mut component = BaseComponent::new("TEST_COMP".to_string(), pins);
|
||||
|
||||
assert!(!component.is_running());
|
||||
|
||||
// Note: In real usage, run() would be called in a separate thread
|
||||
component.set_running(true);
|
||||
assert!(component.is_running());
|
||||
|
||||
component.stop();
|
||||
assert!(!component.is_running());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base_component_clone() {
|
||||
let pins = BaseComponent::create_pin_map(&["A0", "D0"], "ORIG");
|
||||
let original = BaseComponent::new("ORIGINAL".to_string(), pins);
|
||||
original.set_running(true);
|
||||
|
||||
let cloned = original.clone();
|
||||
|
||||
// Cloned component should have same name but not running state
|
||||
assert_eq!(cloned.name(), "ORIGINAL");
|
||||
assert!(!cloned.is_running()); // Clone should not be running
|
||||
|
||||
// Pins should exist but be different instances
|
||||
assert!(cloned.get_pin("A0").is_ok());
|
||||
assert!(cloned.get_pin("D0").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_component_pins() {
|
||||
let pin_definitions = vec![
|
||||
("A0", "input"),
|
||||
("D0", "output"),
|
||||
("VCC", "power"),
|
||||
("GND", "ground"),
|
||||
];
|
||||
|
||||
let pins = create_component_pins("TEST", &pin_definitions);
|
||||
|
||||
assert_eq!(pins.len(), 4);
|
||||
assert!(pins.contains_key("A0"));
|
||||
assert!(pins.contains_key("D0"));
|
||||
assert!(pins.contains_key("VCC"));
|
||||
assert!(pins.contains_key("GND"));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue