<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_upper_case_globals)]

use llvm;
use llvm::{TypeRef, Bool, False, True, TypeKind, ValueRef};
use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128};

use trans::context::CrateContext;
use util::nodemap::FnvHashMap;

use syntax::ast;

use std::ffi::CString;
#[cfg(stage0)]
use std::mem;
use std::ptr;
#[cfg(not(stage0))]
use std::slice;
use std::cell::RefCell;

use libc::c_uint;

#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(C)]
pub struct Type {
    rf: TypeRef
}

macro_rules! ty {
    ($e:expr) => ( Type::from_ref(unsafe { $e }))
}

/// Wrapper for LLVM TypeRef
impl Type {
    #[inline(always)]
    pub fn from_ref(r: TypeRef) -> Type {
        Type {
            rf: r
        }
    }

    #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler
    pub fn to_ref(&self) -> TypeRef {
        self.rf
    }

    pub fn to_string(self: Type) -> String {
        llvm::build_string(|s| unsafe {
            llvm::LLVMWriteTypeToString(self.to_ref(), s);
        }).expect("non-UTF8 type description from LLVM")
    }

    pub fn to_ref_slice(slice: &[Type]) -> &[TypeRef] {
        unsafe { mem::transmute(slice) }
    }

    pub fn void(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMVoidTypeInContext(ccx.llcx()))
    }

    pub fn nil(ccx: &CrateContext) -> Type {
        Type::empty_struct(ccx)
    }

    pub fn metadata(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx()))
    }

    pub fn i1(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt1TypeInContext(ccx.llcx()))
    }

    pub fn i8(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt8TypeInContext(ccx.llcx()))
    }

    pub fn i16(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt16TypeInContext(ccx.llcx()))
    }

    pub fn i32(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt32TypeInContext(ccx.llcx()))
    }

    pub fn i64(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt64TypeInContext(ccx.llcx()))
    }

    // Creates an integer type with the given number of bits, e.g. i24
    pub fn ix(ccx: &CrateContext, num_bits: u64) -> Type {
        ty!(llvm::LLVMIntTypeInContext(ccx.llcx(), num_bits as c_uint))
    }

    pub fn f32(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMFloatTypeInContext(ccx.llcx()))
    }

    pub fn f64(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx()))
    }

    pub fn bool(ccx: &CrateContext) -> Type {
        Type::i8(ccx)
    }

    pub fn char(ccx: &CrateContext) -> Type {
        Type::i32(ccx)
    }

    pub fn i8p(ccx: &CrateContext) -> Type {
        Type::i8(ccx).ptr_to()
    }

    pub fn int(ccx: &CrateContext) -> Type {
        match &ccx.tcx().sess.target.target.target_pointer_width[..] {
            "32" => Type::i32(ccx),
            "64" => Type::i64(ccx),
            tws => panic!("Unsupported target word size for int: {}", tws),
        }
    }

    pub fn int_from_ty(ccx: &CrateContext, t: ast::IntTy) -> Type {
        match t {
            ast::TyIs => ccx.int_type(),
            ast::TyI8 => Type::i8(ccx),
            ast::TyI16 => Type::i16(ccx),
            ast::TyI32 => Type::i32(ccx),
            ast::TyI64 => Type::i64(ccx)
        }
    }

    pub fn uint_from_ty(ccx: &CrateContext, t: ast::UintTy) -> Type {
        match t {
            ast::TyUs => ccx.int_type(),
            ast::TyU8 => Type::i8(ccx),
            ast::TyU16 => Type::i16(ccx),
            ast::TyU32 => Type::i32(ccx),
            ast::TyU64 => Type::i64(ccx)
        }
    }

    pub fn float_from_ty(ccx: &CrateContext, t: ast::FloatTy) -> Type {
        match t {
            ast::TyF32 => Type::f32(ccx),
            ast::TyF64 => Type::f64(ccx),
        }
    }

    pub fn func(args: &[Type], ret: &Type) -> Type {
        let slice: &[TypeRef] = Type::to_ref_slice(args);
        ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(),
                                   args.len() as c_uint, False))
    }

    pub fn variadic_func(args: &[Type], ret: &Type) -> Type {
        let slice: &[TypeRef] = Type::to_ref_slice(args);
        ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(),
                                   args.len() as c_uint, True))
    }

    pub fn struct_(ccx: &CrateContext, els: &[Type], packed: bool) -> Type {
        let els: &[TypeRef] = Type::to_ref_slice(els);
        ty!(llvm::LLVMStructTypeInContext(ccx.llcx(), els.as_ptr(),
                                          els.len() as c_uint,
                                          packed as Bool))
    }

    pub fn named_struct(ccx: &CrateContext, name: &str) -> Type {
        let name = CString::new(name).unwrap();
        ty!(llvm::LLVMStructCreateNamed(ccx.llcx(), name.as_ptr()))
    }

    pub fn empty_struct(ccx: &CrateContext) -> Type {
        Type::struct_(ccx, &[], false)
    }

    pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type {
        Type::func(&[t], &Type::void(ccx))
    }

    pub fn array(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMRustArrayType(ty.to_ref(), len))
    }

    pub fn vector(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint))
    }

    pub fn vec(ccx: &CrateContext, ty: &Type) -> Type {
        Type::struct_(ccx,
            &[Type::array(ty, 0), Type::int(ccx)],
        false)
    }

    pub fn opaque_vec(ccx: &CrateContext) -> Type {
        Type::vec(ccx, &Type::i8(ccx))
    }

    pub fn vtable_ptr(ccx: &CrateContext) -> Type {
        Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to()
    }

    pub fn kind(&self) -> TypeKind {
        unsafe {
            llvm::LLVMGetTypeKind(self.to_ref())
        }
    }

    pub fn set_struct_body(&mut self, els: &[Type], packed: bool) {
        let slice: &[TypeRef] = Type::to_ref_slice(els);
        unsafe {
            llvm::LLVMStructSetBody(self.to_ref(), slice.as_ptr(),
                                    els.len() as c_uint, packed as Bool)
        }
    }

    pub fn ptr_to(&self) -> Type {
        ty!(llvm::LLVMPointerType(self.to_ref(), 0))
    }

    pub fn is_aggregate(&self) -> bool {
        match self.kind() {
            TypeKind::Struct | TypeKind::Array => true,
            _ =>  false
        }
    }

    pub fn is_packed(&self) -> bool {
        unsafe {
            llvm::LLVMIsPackedStruct(self.to_ref()) == True
        }
    }

    pub fn element_type(&self) -> Type {
        unsafe {
            Type::from_ref(llvm::LLVMGetElementType(self.to_ref()))
        }
    }

    /// Return the number of elements in `self` if it is a LLVM vector type.
    pub fn vector_length(&self) -> usize {
        unsafe {
            llvm::LLVMGetVectorSize(self.to_ref()) as usize
        }
    }

    pub fn array_length(&self) -> usize {
        unsafe {
            llvm::LLVMGetArrayLength(self.to_ref()) as usize
        }
    }

    pub fn field_types(&self) -> Vec<Type> {
        unsafe {
            let n_elts = llvm::LLVMCountStructElementTypes(self.to_ref()) as usize;
            if n_elts == 0 {
                return Vec::new();
            }
            let mut elts = vec![Type { rf: ptr::null_mut() }; n_elts];
            llvm::LLVMGetStructElementTypes(self.to_ref(),
                                            elts.as_mut_ptr() as *mut TypeRef);
            elts
        }
    }

    pub fn return_type(&self) -> Type {
        ty!(llvm::LLVMGetReturnType(self.to_ref()))
    }

    pub fn func_params(&self) -> Vec<Type> {
        unsafe {
            let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as usize;
            let mut args = vec![Type { rf: ptr::null_mut() }; n_args];
            llvm::LLVMGetParamTypes(self.to_ref(),
                                    args.as_mut_ptr() as *mut TypeRef);
            args
        }
    }

    pub fn float_width(&self) -> usize {
        match self.kind() {
            Float => 32,
            Double => 64,
            X86_FP80 => 80,
            FP128 | PPC_FP128 => 128,
            _ => panic!("llvm_float_width called on a non-float type")
        }
    }

    /// Retrieve the bit width of the integer type `self`.
    pub fn int_width(&self) -> u64 {
        unsafe {
            llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64
        }
    }
}

/* Memory-managed object interface to type handles. */

pub struct TypeNames {
    named_types: RefCell<FnvHashMap<String, TypeRef>>,
}

impl TypeNames {
    pub fn new() -> TypeNames {
        TypeNames {
            named_types: RefCell::new(FnvHashMap())
        }
    }

    pub fn associate_type(&self, s: &str, t: &Type) {
        assert!(self.named_types.borrow_mut().insert(s.to_string(),
                                                     t.to_ref()).is_none());
    }

    pub fn find_type(&self, s: &str) -> Option<Type> {
        self.named_types.borrow().get(s).map(|x| Type::from_ref(*x))
    }

    pub fn type_to_string(&self, ty: Type) -> String {
        ty.to_string()
    }

    pub fn types_to_str(&self, tys: &[Type]) -> String {
        let strs: Vec<String> = tys.iter().map(|t| self.type_to_string(*t)).collect();
        format!("[{}]", strs.join(","))
    }

    pub fn val_to_string(&self, val: ValueRef) -> String {
        llvm::build_string(|s| unsafe {
                llvm::LLVMWriteValueToString(val, s);
            }).expect("nun-UTF8 value description from LLVM")
    }
}
<commit_msg>Remove #[cfg(stage0)] annotation<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_upper_case_globals)]

use llvm;
use llvm::{TypeRef, Bool, False, True, TypeKind, ValueRef};
use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128};

use trans::context::CrateContext;
use util::nodemap::FnvHashMap;

use syntax::ast;

use std::ffi::CString;
use std::mem;
use std::ptr;
use std::cell::RefCell;

use libc::c_uint;

#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(C)]
pub struct Type {
    rf: TypeRef
}

macro_rules! ty {
    ($e:expr) => ( Type::from_ref(unsafe { $e }))
}

/// Wrapper for LLVM TypeRef
impl Type {
    #[inline(always)]
    pub fn from_ref(r: TypeRef) -> Type {
        Type {
            rf: r
        }
    }

    #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler
    pub fn to_ref(&self) -> TypeRef {
        self.rf
    }

    pub fn to_string(self: Type) -> String {
        llvm::build_string(|s| unsafe {
            llvm::LLVMWriteTypeToString(self.to_ref(), s);
        }).expect("non-UTF8 type description from LLVM")
    }

    pub fn to_ref_slice(slice: &[Type]) -> &[TypeRef] {
        unsafe { mem::transmute(slice) }
    }

    pub fn void(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMVoidTypeInContext(ccx.llcx()))
    }

    pub fn nil(ccx: &CrateContext) -> Type {
        Type::empty_struct(ccx)
    }

    pub fn metadata(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx()))
    }

    pub fn i1(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt1TypeInContext(ccx.llcx()))
    }

    pub fn i8(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt8TypeInContext(ccx.llcx()))
    }

    pub fn i16(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt16TypeInContext(ccx.llcx()))
    }

    pub fn i32(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt32TypeInContext(ccx.llcx()))
    }

    pub fn i64(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMInt64TypeInContext(ccx.llcx()))
    }

    // Creates an integer type with the given number of bits, e.g. i24
    pub fn ix(ccx: &CrateContext, num_bits: u64) -> Type {
        ty!(llvm::LLVMIntTypeInContext(ccx.llcx(), num_bits as c_uint))
    }

    pub fn f32(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMFloatTypeInContext(ccx.llcx()))
    }

    pub fn f64(ccx: &CrateContext) -> Type {
        ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx()))
    }

    pub fn bool(ccx: &CrateContext) -> Type {
        Type::i8(ccx)
    }

    pub fn char(ccx: &CrateContext) -> Type {
        Type::i32(ccx)
    }

    pub fn i8p(ccx: &CrateContext) -> Type {
        Type::i8(ccx).ptr_to()
    }

    pub fn int(ccx: &CrateContext) -> Type {
        match &ccx.tcx().sess.target.target.target_pointer_width[..] {
            "32" => Type::i32(ccx),
            "64" => Type::i64(ccx),
            tws => panic!("Unsupported target word size for int: {}", tws),
        }
    }

    pub fn int_from_ty(ccx: &CrateContext, t: ast::IntTy) -> Type {
        match t {
            ast::TyIs => ccx.int_type(),
            ast::TyI8 => Type::i8(ccx),
            ast::TyI16 => Type::i16(ccx),
            ast::TyI32 => Type::i32(ccx),
            ast::TyI64 => Type::i64(ccx)
        }
    }

    pub fn uint_from_ty(ccx: &CrateContext, t: ast::UintTy) -> Type {
        match t {
            ast::TyUs => ccx.int_type(),
            ast::TyU8 => Type::i8(ccx),
            ast::TyU16 => Type::i16(ccx),
            ast::TyU32 => Type::i32(ccx),
            ast::TyU64 => Type::i64(ccx)
        }
    }

    pub fn float_from_ty(ccx: &CrateContext, t: ast::FloatTy) -> Type {
        match t {
            ast::TyF32 => Type::f32(ccx),
            ast::TyF64 => Type::f64(ccx),
        }
    }

    pub fn func(args: &[Type], ret: &Type) -> Type {
        let slice: &[TypeRef] = Type::to_ref_slice(args);
        ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(),
                                   args.len() as c_uint, False))
    }

    pub fn variadic_func(args: &[Type], ret: &Type) -> Type {
        let slice: &[TypeRef] = Type::to_ref_slice(args);
        ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(),
                                   args.len() as c_uint, True))
    }

    pub fn struct_(ccx: &CrateContext, els: &[Type], packed: bool) -> Type {
        let els: &[TypeRef] = Type::to_ref_slice(els);
        ty!(llvm::LLVMStructTypeInContext(ccx.llcx(), els.as_ptr(),
                                          els.len() as c_uint,
                                          packed as Bool))
    }

    pub fn named_struct(ccx: &CrateContext, name: &str) -> Type {
        let name = CString::new(name).unwrap();
        ty!(llvm::LLVMStructCreateNamed(ccx.llcx(), name.as_ptr()))
    }

    pub fn empty_struct(ccx: &CrateContext) -> Type {
        Type::struct_(ccx, &[], false)
    }

    pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type {
        Type::func(&[t], &Type::void(ccx))
    }

    pub fn array(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMRustArrayType(ty.to_ref(), len))
    }

    pub fn vector(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint))
    }

    pub fn vec(ccx: &CrateContext, ty: &Type) -> Type {
        Type::struct_(ccx,
            &[Type::array(ty, 0), Type::int(ccx)],
        false)
    }

    pub fn opaque_vec(ccx: &CrateContext) -> Type {
        Type::vec(ccx, &Type::i8(ccx))
    }

    pub fn vtable_ptr(ccx: &CrateContext) -> Type {
        Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to()
    }

    pub fn kind(&self) -> TypeKind {
        unsafe {
            llvm::LLVMGetTypeKind(self.to_ref())
        }
    }

    pub fn set_struct_body(&mut self, els: &[Type], packed: bool) {
        let slice: &[TypeRef] = Type::to_ref_slice(els);
        unsafe {
            llvm::LLVMStructSetBody(self.to_ref(), slice.as_ptr(),
                                    els.len() as c_uint, packed as Bool)
        }
    }

    pub fn ptr_to(&self) -> Type {
        ty!(llvm::LLVMPointerType(self.to_ref(), 0))
    }

    pub fn is_aggregate(&self) -> bool {
        match self.kind() {
            TypeKind::Struct | TypeKind::Array => true,
            _ =>  false
        }
    }

    pub fn is_packed(&self) -> bool {
        unsafe {
            llvm::LLVMIsPackedStruct(self.to_ref()) == True
        }
    }

    pub fn element_type(&self) -> Type {
        unsafe {
            Type::from_ref(llvm::LLVMGetElementType(self.to_ref()))
        }
    }

    /// Return the number of elements in `self` if it is a LLVM vector type.
    pub fn vector_length(&self) -> usize {
        unsafe {
            llvm::LLVMGetVectorSize(self.to_ref()) as usize
        }
    }

    pub fn array_length(&self) -> usize {
        unsafe {
            llvm::LLVMGetArrayLength(self.to_ref()) as usize
        }
    }

    pub fn field_types(&self) -> Vec<Type> {
        unsafe {
            let n_elts = llvm::LLVMCountStructElementTypes(self.to_ref()) as usize;
            if n_elts == 0 {
                return Vec::new();
            }
            let mut elts = vec![Type { rf: ptr::null_mut() }; n_elts];
            llvm::LLVMGetStructElementTypes(self.to_ref(),
                                            elts.as_mut_ptr() as *mut TypeRef);
            elts
        }
    }

    pub fn return_type(&self) -> Type {
        ty!(llvm::LLVMGetReturnType(self.to_ref()))
    }

    pub fn func_params(&self) -> Vec<Type> {
        unsafe {
            let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as usize;
            let mut args = vec![Type { rf: ptr::null_mut() }; n_args];
            llvm::LLVMGetParamTypes(self.to_ref(),
                                    args.as_mut_ptr() as *mut TypeRef);
            args
        }
    }

    pub fn float_width(&self) -> usize {
        match self.kind() {
            Float => 32,
            Double => 64,
            X86_FP80 => 80,
            FP128 | PPC_FP128 => 128,
            _ => panic!("llvm_float_width called on a non-float type")
        }
    }

    /// Retrieve the bit width of the integer type `self`.
    pub fn int_width(&self) -> u64 {
        unsafe {
            llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64
        }
    }
}

/* Memory-managed object interface to type handles. */

pub struct TypeNames {
    named_types: RefCell<FnvHashMap<String, TypeRef>>,
}

impl TypeNames {
    pub fn new() -> TypeNames {
        TypeNames {
            named_types: RefCell::new(FnvHashMap())
        }
    }

    pub fn associate_type(&self, s: &str, t: &Type) {
        assert!(self.named_types.borrow_mut().insert(s.to_string(),
                                                     t.to_ref()).is_none());
    }

    pub fn find_type(&self, s: &str) -> Option<Type> {
        self.named_types.borrow().get(s).map(|x| Type::from_ref(*x))
    }

    pub fn type_to_string(&self, ty: Type) -> String {
        ty.to_string()
    }

    pub fn types_to_str(&self, tys: &[Type]) -> String {
        let strs: Vec<String> = tys.iter().map(|t| self.type_to_string(*t)).collect();
        format!("[{}]", strs.join(","))
    }

    pub fn val_to_string(&self, val: ValueRef) -> String {
        llvm::build_string(|s| unsafe {
                llvm::LLVMWriteValueToString(val, s);
            }).expect("nun-UTF8 value description from LLVM")
    }
}
<|endoftext|>


<commit_before>use {HashSet};
use std::collections::{HashMap, RingBuf, Deque};

// call this Unigraph instead?
pub trait Graph<T> {
    /// Insert a new node, returning its index
    fn add_node(&mut self, val: T) -> NodeIndex;

    /// Insert a new edge, returning an error if one of the indices is invalid
    /// and a result otherwise
    fn add_edge(&mut self, i: NodeIndex, j: NodeIndex) -> GraphResult<bool>;

    /// Return an iterator over the out-neighbors for a given node
    fn adj(&self, i: NodeIndex) -> NodeIndices;

    /// Return the number of nodes in the graph
    fn num_nodes(&self) -> uint;

    fn node_indices(&self) -> NodeIndices;

    fn dfs(&self) -> Digraph<NodeIndex> {
        let mut tree = Digraph::new();

        if self.num_nodes() == 0 {
            return tree;
        }

        let mut visited = HashSet::new();
        let mut discovered = vec!();

        discovered.push((0, None));
        loop {
            match discovered.pop() {
                None => break,
                Some((ind, parent)) => {
                    tree.add_node(ind);
                    if parent.is_some() {
                        tree.add_edge(parent.unwrap(), ind);
                    }
                    visited.insert(ind);

                    for i in self.adj(ind) {
                        if !visited.contains(i) {
                            discovered.push((*i, Some(ind)));
                        }
                    }
                }
            }
        }
        return tree;

    }
}

struct GraphError {
    error_type: GraphErrorType,
}

impl GraphError {
    fn invalid_index(ind: NodeIndex) -> GraphError {
        GraphError { error_type: InvalidNodeIndex(ind) }
    }
}

type GraphResult<T> = Result<T, GraphError>;

enum GraphErrorType {
    InvalidNodeIndex(NodeIndex),
}

type NodeIndex = uint;

#[deriving(Show, Clone)]
struct Node<T> {
    data: T,
    index: NodeIndex,
}

/// Undirected graph. Allows loops.
#[deriving(Show)]
pub struct UnGraph<T> {
    nodes: Vec<Node<T>>,
    adj: Vec<HashSet<NodeIndex>>,
    num_nodes: uint,
}

/// Directed graph. Allows loops.
#[deriving(Show)]
pub struct Digraph<T> {
    nodes: Vec<Node<T>>,
    in_adj: Vec<HashSet<NodeIndex>>,
    out_adj: Vec<HashSet<NodeIndex>>,
    num_nodes: uint,
}

impl<T> UnGraph<T> {
    pub fn new() -> UnGraph<T> {
        UnGraph { nodes: vec!(), adj: vec!(), num_nodes: 0 }
    }

    pub fn degree(&mut self, ind: NodeIndex) -> Result<uint, GraphError> {
        if ind >= self.num_nodes {
            Err(GraphError::invalid_index(ind))
        } else {
            Ok(self.adj[ind].len())
        }
    }
}
impl<T: Clone> UnGraph<T> {
    /// Returns a new graph induced by a set of node indices
    pub fn induced_subgraph(&self, nodes: &HashSet<NodeIndex>) -> UnGraph<T> {
        let mut new = UnGraph::new();
        let mut ind_map = HashMap::new();

        for (i, ind) in nodes.iter().enumerate() {
            ind_map.insert(ind, i);
            new.add_node(self.nodes[*ind].data.clone());
        }

        // Here we're assuming that NodeIndex = uint. Not sure how to easily do otherwise
        for i in range(0, self.num_nodes) {
            for j in self.adj[i].iter() {
                new.add_edge(i, *j);
            }
        }

        new
    }
}

impl<T> Graph<T> for UnGraph<T> {
    fn add_node(&mut self, val: T) -> NodeIndex {
        let ind = self.num_nodes;
        self.nodes.push(Node { data: val, index: ind });
        self.adj.push(HashSet::new());
        self.num_nodes += 1;
        ind
    }

    fn add_edge(&mut self, i: NodeIndex, j: NodeIndex) -> GraphResult<bool> {
        if i >= self.num_nodes {
            Err(GraphError::invalid_index(i))
        } else if j >= self.num_nodes {
            Err(GraphError::invalid_index(j))
        } else {
            if self.adj[i].contains(&j) {
                Ok(false)
            } else {
                self.adj.get_mut(i).insert(j);
                self.adj.get_mut(j).insert(i);
                Ok(true)
            }
        }
    }

    fn adj(&self, i: NodeIndex) -> NodeIndices {
        NodeIndices::from_set(&self.adj[i])
    }

    fn num_nodes(&self) -> uint {
        self.num_nodes
    }

    fn node_indices(&self) -> NodeIndices {
        NodeIndices::from_node_vec(&self.nodes)
    }
}

impl<T> Digraph<T> {
    pub fn new() -> Digraph<T> {
        Digraph { nodes: vec!(), in_adj: vec!(), out_adj: vec!(), num_nodes: 0 }
    }

    pub fn out_degree(&mut self, ind: NodeIndex) -> Result<uint, GraphError> {
        if ind >= self.num_nodes {
            Err(GraphError::invalid_index(ind))
        } else {
            Ok(self.out_adj[ind].len())
        }
    }

    pub fn in_degree(&mut self, ind: NodeIndex) -> Result<uint, GraphError> {
        if ind >= self.num_nodes {
            Err(GraphError::invalid_index(ind))
        } else {
            Ok(self.in_adj[ind].len())
        }
    }
}

impl<T: Clone> Digraph<T> {
    /// Returns a new graph induced by a set of node indices
    pub fn induced_subgraph(&self, nodes: &HashSet<NodeIndex>) -> Digraph<T> {
        let mut new = Digraph::new();
        let mut ind_map = HashMap::new();

        for (i, ind) in nodes.iter().enumerate() {
            ind_map.insert(ind, i);
            new.add_node(self.nodes[*ind].data.clone());
        }

        // Here we're assuming that NodeIndex = uint. Not sure how to easily do otherwise
        for i in range(0, self.num_nodes) {
            for j in self.in_adj[i].iter() {
                new.add_edge(*j, i);
            }

            for j in self.out_adj[i].iter() {
                new.add_edge(i, *j);
            }
        }

        new
    }
}

impl<T> Graph<T> for Digraph<T> {
    fn add_node(&mut self, val: T) -> NodeIndex {
        let ind = self.num_nodes;
        self.nodes.push(Node { data: val, index: ind });
        self.in_adj.push(HashSet::new());
        self.out_adj.push(HashSet::new());
        self.num_nodes += 1;
        ind
    }

    fn add_edge(&mut self, i: NodeIndex, j: NodeIndex) -> GraphResult<bool> {
        if i >= self.num_nodes {
            Err(GraphError::invalid_index(i))
        } else if j >= self.num_nodes {
            Err(GraphError::invalid_index(j))
        } else {
            if self.out_adj[i].contains(&j) {
                Ok(false)
            } else {
                self.out_adj.get_mut(i).insert(j);
                self.in_adj.get_mut(j).insert(i);
                Ok(true)
            }
        }
    }

    fn adj(&self, i: NodeIndex) -> NodeIndices {
        NodeIndices::from_set(&self.out_adj[i])
    }

    fn num_nodes(&self) -> uint {
        self.num_nodes
    }

    fn node_indices(&self) -> NodeIndices {
        NodeIndices::from_node_vec(&self.nodes)
    }
}

struct NodeIndices {
    indices: Vec<NodeIndex>,
    curr: uint,
}

impl Iterator<NodeIndex> for NodeIndices {
    fn next(&mut self) -> Option<NodeIndex> {
        if self.curr < self.indices.len() {
            self.curr += 1;
            Some(self.indices[self.curr - 1])
        } else {
            None
        }
    }
}

impl NodeIndices {
    fn from_set(set: &HashSet<NodeIndex>) -> NodeIndices {
        let mut vec = vec!();
        for &i in set.iter() {
            vec.push(i);
        }
        NodeIndices { indices: vec, curr: 0 }
    }

    fn from_node_vec<T>(vec: &Vec<Node<T>>) -> NodeIndices {
        let mut new = vec!();
        for n in vec.iter() {
            new.push(n.index);
        }

        NodeIndices { indices: new, curr: 0 }
    }
}
<commit_msg>Add a type alias for a set of node indices<commit_after>use {HashSet};
use std::collections::{HashMap, RingBuf, Deque};

// call this Unigraph instead?
pub trait Graph<T> {
    /// Insert a new node, returning its index
    fn add_node(&mut self, val: T) -> NodeIndex;

    /// Insert a new edge, returning an error if one of the indices is invalid
    /// and a result otherwise
    fn add_edge(&mut self, i: NodeIndex, j: NodeIndex) -> GraphResult<bool>;

    /// Return an iterator over the out-neighbors for a given node
    fn adj(&self, i: NodeIndex) -> NodeIndices;

    /// Return the number of nodes in the graph
    fn num_nodes(&self) -> uint;

    fn node_indices(&self) -> NodeIndices;

    fn dfs(&self) -> Digraph<NodeIndex> {
        let mut tree = Digraph::new();

        if self.num_nodes() == 0 {
            return tree;
        }

        let mut visited = HashSet::new();
        let mut discovered = vec!();

        discovered.push((0, None));
        loop {
            match discovered.pop() {
                None => break,
                Some((ind, parent)) => {
                    tree.add_node(ind);
                    if parent.is_some() {
                        tree.add_edge(parent.unwrap(), ind);
                    }
                    visited.insert(ind);

                    for i in self.adj(ind) {
                        if !visited.contains(i) {
                            discovered.push((*i, Some(ind)));
                        }
                    }
                }
            }
        }
        return tree;

    }
}

struct GraphError {
    error_type: GraphErrorType,
}

impl GraphError {
    fn invalid_index(ind: NodeIndex) -> GraphError {
        GraphError { error_type: InvalidNodeIndex(ind) }
    }
}

type GraphResult<T> = Result<T, GraphError>;

enum GraphErrorType {
    InvalidNodeIndex(NodeIndex),
}

pub type NodeIndex = uint;
type NodeIndexSet = HashSet<NodeIndex>;

#[deriving(Show, Clone)]
struct Node<T> {
    data: T,
    index: NodeIndex,
}

/// Undirected graph. Allows loops.
#[deriving(Show)]
pub struct UnGraph<T> {
    nodes: Vec<Node<T>>,
    adj: Vec<NodeIndexSet>,
    num_nodes: uint,
}

/// Directed graph. Allows loops.
#[deriving(Show)]
pub struct Digraph<T> {
    nodes: Vec<Node<T>>,
    in_adj: Vec<NodeIndexSet>,
    out_adj: Vec<NodeIndexSet>,
    num_nodes: uint,
}

impl<T> UnGraph<T> {
    pub fn new() -> UnGraph<T> {
        UnGraph { nodes: vec!(), adj: vec!(), num_nodes: 0 }
    }

    pub fn degree(&mut self, ind: NodeIndex) -> Result<uint, GraphError> {
        if ind >= self.num_nodes {
            Err(GraphError::invalid_index(ind))
        } else {
            Ok(self.adj[ind].len())
        }
    }
}
impl<T: Clone> UnGraph<T> {
    /// Returns a new graph induced by a set of node indices
    pub fn induced_subgraph(&self, nodes: &NodeIndexSet) -> UnGraph<T> {
        let mut new = UnGraph::new();
        let mut ind_map = HashMap::new();

        for (i, ind) in nodes.iter().enumerate() {
            ind_map.insert(ind, i);
            new.add_node(self.nodes[*ind].data.clone());
        }

        // Here we're assuming that NodeIndex = uint. Not sure how to easily do otherwise
        for i in range(0, self.num_nodes) {
            for j in self.adj[i].iter() {
                new.add_edge(i, *j);
            }
        }

        new
    }
}

impl<T> Graph<T> for UnGraph<T> {
    fn add_node(&mut self, val: T) -> NodeIndex {
        let ind = self.num_nodes;
        self.nodes.push(Node { data: val, index: ind });
        self.adj.push(HashSet::new());
        self.num_nodes += 1;
        ind
    }

    fn add_edge(&mut self, i: NodeIndex, j: NodeIndex) -> GraphResult<bool> {
        if i >= self.num_nodes {
            Err(GraphError::invalid_index(i))
        } else if j >= self.num_nodes {
            Err(GraphError::invalid_index(j))
        } else {
            if self.adj[i].contains(&j) {
                Ok(false)
            } else {
                self.adj.get_mut(i).insert(j);
                self.adj.get_mut(j).insert(i);
                Ok(true)
            }
        }
    }

    fn adj(&self, i: NodeIndex) -> NodeIndices {
        NodeIndices::from_set(&self.adj[i])
    }

    fn num_nodes(&self) -> uint {
        self.num_nodes
    }

    fn node_indices(&self) -> NodeIndices {
        NodeIndices::from_node_vec(&self.nodes)
    }
}

impl<T> Digraph<T> {
    pub fn new() -> Digraph<T> {
        Digraph { nodes: vec!(), in_adj: vec!(), out_adj: vec!(), num_nodes: 0 }
    }

    pub fn out_degree(&mut self, ind: NodeIndex) -> Result<uint, GraphError> {
        if ind >= self.num_nodes {
            Err(GraphError::invalid_index(ind))
        } else {
            Ok(self.out_adj[ind].len())
        }
    }

    pub fn in_degree(&mut self, ind: NodeIndex) -> Result<uint, GraphError> {
        if ind >= self.num_nodes {
            Err(GraphError::invalid_index(ind))
        } else {
            Ok(self.in_adj[ind].len())
        }
    }
}

impl<T: Clone> Digraph<T> {
    /// Returns a new graph induced by a set of node indices
    pub fn induced_subgraph(&self, nodes: &NodeIndexSet) -> Digraph<T> {
        let mut new = Digraph::new();
        let mut ind_map = HashMap::new();

        for (i, ind) in nodes.iter().enumerate() {
            ind_map.insert(ind, i);
            new.add_node(self.nodes[*ind].data.clone());
        }

        // Here we're assuming that NodeIndex = uint. Not sure how to easily do otherwise
        for i in range(0, self.num_nodes) {
            for j in self.in_adj[i].iter() {
                new.add_edge(*j, i);
            }

            for j in self.out_adj[i].iter() {
                new.add_edge(i, *j);
            }
        }

        new
    }
}

impl<T> Graph<T> for Digraph<T> {
    fn add_node(&mut self, val: T) -> NodeIndex {
        let ind = self.num_nodes;
        self.nodes.push(Node { data: val, index: ind });
        self.in_adj.push(HashSet::new());
        self.out_adj.push(HashSet::new());
        self.num_nodes += 1;
        ind
    }

    fn add_edge(&mut self, i: NodeIndex, j: NodeIndex) -> GraphResult<bool> {
        if i >= self.num_nodes {
            Err(GraphError::invalid_index(i))
        } else if j >= self.num_nodes {
            Err(GraphError::invalid_index(j))
        } else {
            if self.out_adj[i].contains(&j) {
                Ok(false)
            } else {
                self.out_adj.get_mut(i).insert(j);
                self.in_adj.get_mut(j).insert(i);
                Ok(true)
            }
        }
    }

    fn adj(&self, i: NodeIndex) -> NodeIndices {
        NodeIndices::from_set(&self.out_adj[i])
    }

    fn num_nodes(&self) -> uint {
        self.num_nodes
    }

    fn node_indices(&self) -> NodeIndices {
        NodeIndices::from_node_vec(&self.nodes)
    }
}

struct NodeIndices {
    indices: Vec<NodeIndex>,
    curr: uint,
}

impl Iterator<NodeIndex> for NodeIndices {
    fn next(&mut self) -> Option<NodeIndex> {
        if self.curr < self.indices.len() {
            self.curr += 1;
            Some(self.indices[self.curr - 1])
        } else {
            None
        }
    }
}

impl NodeIndices {
    fn from_set(set: &NodeIndexSet) -> NodeIndices {
        let mut vec = vec!();
        for &i in set.iter() {
            vec.push(i);
        }
        NodeIndices { indices: vec, curr: 0 }
    }

    fn from_node_vec<T>(vec: &Vec<Node<T>>) -> NodeIndices {
        let mut new = vec!();
        for n in vec.iter() {
            new.push(n.index);
        }

        NodeIndices { indices: new, curr: 0 }
    }
}
<|endoftext|>


<commit_before>use cairo;
use gdk::{self, WindowExt};
use gtk::{self, BoxExt, ContainerExt, DrawingArea, ScrolledWindowExt, StateFlags, WidgetExt};
use std::cell::RefCell;

use std::rc::Rc;

use color::Color;
use utils::RotateVec;

const LEFT_WIDTH: f64 = 31.;

pub struct Graph {
    colors: Vec<Color>,
    pub data: Vec<RotateVec<f64>>,
    vertical_layout: gtk::Box,
    scroll_layout: gtk::ScrolledWindow,
    horizontal_layout: gtk::Box,
    pub area: DrawingArea,
    max: Option<RefCell<f64>>,
    keep_max: bool,
    display_labels: RefCell<bool>,
    initial_diff: Option<i32>,
    label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    labels_layout_width: i32,
    /// `minimum` is used only if `max` is set: it'll be the minimum that the `max` value will
    /// be able to go down.
    minimum: Option<f64>,
    // In %, from 0 to whatever
    overhead: Option<f64>,
}

impl Graph {
    /// If `max` is `None`, the graph will expect values between 0 and 1.
    ///
    /// If `keep_max` is set to `true`, then this value will never go down, meaning that graphs
    /// won't rescale down. It is not taken into account if `max` is `None`.
    pub fn new(max: Option<f64>, keep_max: bool) -> Graph {
        let g = Graph {
            colors: vec![],
            data: vec![],
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0),
            scroll_layout: gtk::ScrolledWindow::new(
                None::<&gtk::Adjustment>,
                None::<&gtk::Adjustment>,
            ),
            horizontal_layout: gtk::Box::new(gtk::Orientation::Horizontal, 0),
            area: DrawingArea::new(),
            max: if let Some(max) = max {
                Some(RefCell::new(max))
            } else {
                None
            },
            keep_max,
            display_labels: RefCell::new(true),
            initial_diff: None,
            label_callbacks: None,
            labels_layout_width: 80,
            minimum: None,
            overhead: None,
        };
        g.scroll_layout.set_min_content_width(g.labels_layout_width);
        g.scroll_layout.add(&g.vertical_layout);
        g.horizontal_layout.pack_start(&g.area, true, true, 0);
        g.horizontal_layout
            .pack_start(&g.scroll_layout, false, true, 10);
        g.horizontal_layout.set_margin_start(5);
        g
    }

    pub fn set_minimum(&mut self, minimum: Option<f64>) {
        self.minimum = minimum;
    }

    pub fn set_overhead(&mut self, overhead: Option<f64>) {
        if let Some(o) = overhead {
            assert!(o >= 0.);
        }
        self.overhead = overhead;
    }

    /// Changes the size of the layout containing labels (the one on the right).
    pub fn set_labels_width(&mut self, labels_layout_width: u32) {
        self.scroll_layout
            .set_min_content_width(labels_layout_width as i32);
        self.labels_layout_width = labels_layout_width as i32;
    }

    pub fn set_label_callbacks(
        &mut self,
        label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    ) {
        self.label_callbacks = label_callbacks;
    }

    pub fn set_display_labels(&self, display_labels: bool) {
        *self.display_labels.borrow_mut() = display_labels;
        if display_labels {
            self.scroll_layout.show_all();
        } else {
            self.scroll_layout.hide();
        }
        self.invalidate();
    }

    pub fn hide(&self) {
        self.horizontal_layout.hide();
    }

    pub fn show_all(&self) {
        self.horizontal_layout.show_all();
        if !*self.display_labels.borrow() {
            self.scroll_layout.hide();
        }
    }

    pub fn attach_to(&self, to: &gtk::Box) {
        to.add(&self.horizontal_layout);
    }

    pub fn push(&mut self, d: RotateVec<f64>, s: &str, override_color: Option<usize>) {
        let c = if let Some(over) = override_color {
            Color::generate(over)
        } else {
            Color::generate(self.data.len() + 11)
        };
        let l = gtk::Label::new(Some(s));
        l.override_color(
            StateFlags::from_bits(0).expect("from_bits failed"),
            Some(&c.to_gdk()),
        );
        self.vertical_layout.add(&l);
        self.colors.push(c);
        self.data.push(d);
    }

    fn draw_labels(&self, c: &cairo::Context, max: f64, height: f64) {
        if let Some(ref call) = self.label_callbacks {
            let entries = call(max);
            let font_size = 8.;

            c.set_source_rgb(0., 0., 0.);
            c.set_font_size(font_size);

            c.move_to(LEFT_WIDTH - 4. - entries[0].len() as f64 * 4., font_size);
            c.show_text(entries[0].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[1].len() as f64 * 4., height / 2.);
            c.show_text(entries[1].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[2].len() as f64 * 4., height - 2.);
            c.show_text(entries[2].as_str());

            c.move_to(
                font_size - 1.,
                height / 2. + 4. * (entries[3].len() >> 1) as f64,
            );
            c.rotate(-::std::f64::consts::FRAC_PI_2);
            c.show_text(entries[3].as_str());
        }
    }

    pub fn draw(&self, c: &cairo::Context, width: f64, height: f64) {
        let x_start = if self.label_callbacks.is_some() {
            LEFT_WIDTH
        } else {
            1.0
        };

        // to limit line "fuzziness"
        #[inline]
        fn rounder(x: f64) -> f64 {
            let fract = x.fract();
            if fract < 0.5 {
                x.trunc()
            } else {
                x.trunc() + 1.
            }
        }

        c.set_source_rgb(0., 0., 0.);
        c.rectangle(x_start, 0., width, height);
        c.fill();
        c.set_source_rgb(0.5, 0.5, 0.5);
        c.set_line_width(0.5);

        // For now it's always 60 seconds.
        let time = 60.;
        let x_step = (width - 2.0 - x_start) * 5.0 / (time as f64);
        let mut current = width * (x_step / 5.0) - 1.0;
        if x_step < 0.1 {
            c.stroke();
            return;
        }

        while current > x_start {
            c.move_to(rounder(current), 0.0);
            c.line_to(rounder(current), height);
            current -= x_step;
        }
        let step = height / 10.0;
        current = step - 1.0;
        while current < height - 1. {
            c.move_to(x_start, rounder(current));
            c.line_to(width, rounder(current));
            current += step;
        }
        c.stroke();

        c.set_line_width(1.);

        if let Some(ref self_max) = self.max {
            let mut max = if self.keep_max {
                *self_max.borrow()
            } else {
                1.
            };
            let len = self.data[0].len() - 1;
            for x in 0..len {
                for entry in &self.data {
                    if entry[x] > max {
                        max = entry[x];
                    }
                }
            }
            if let Some(min) = self.minimum {
                if min > max {
                    max = min;
                }
            } else if let Some(over) = self.overhead {
                max = max + max * over / 100.;
            }
            if !self.data.is_empty() && !self.data[0].is_empty() {
                let len = self.data[0].len() - 1;
                let step = (width - 2.0 - x_start) / len as f64;
                current = x_start + 1.0;
                let mut index = len;
                while current > x_start && index > 0 {
                    for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                        c.set_source_rgb(color.r, color.g, color.b);
                        c.move_to(
                            current + step,
                            height - entry[index - 1] / max * (height - 1.0),
                        );
                        c.line_to(current, height - entry[index] / max * (height - 1.0));
                        c.stroke();
                    }
                    current += step;
                    index -= 1;
                }
            }
            if max > *self_max.borrow() || !self.keep_max {
                *self_max.borrow_mut() = max;
            }
            self.draw_labels(c, max, height);
        } else if !self.data.is_empty() && !self.data[0].is_empty() {
            let len = self.data[0].len() - 1;
            let step = (width - 2.0 - x_start) / (len as f64);
            current = x_start + 1.0;
            let mut index = len;
            while current > x_start && index > 0 {
                for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                    c.set_source_rgb(color.r, color.g, color.b);
                    c.move_to(current + step, height - entry[index - 1] * (height - 1.0));
                    c.line_to(current, height - entry[index] * (height - 1.0));
                    c.stroke();
                }
                current += step;
                index -= 1;
            }
            // To be called in last to avoid having to restore state (rotation).
            self.draw_labels(c, 100., height);
        }
    }

    pub fn invalidate(&self) {
        if let Some(t_win) = self.area.get_window() {
            let (x, y) = self
                .area
                .translate_coordinates(&self.area, 0, 0)
                .expect("translate_coordinates failed");
            let rect = gdk::Rectangle {
                x,
                y,
                width: self.area.get_allocated_width(),
                height: self.area.get_allocated_height(),
            };
            t_win.invalidate_rect(Some(&rect), true);
        }
    }

    pub fn send_size_request(&self, width: Option<i32>) {
        let mut width = match width {
            Some(w) => w,
            None => {
                if let Some(parent) = self.area.get_parent() {
                    parent.get_allocation().width
                        - parent.get_margin_start()
                        - parent.get_margin_end()
                } else {
                    eprintln!(
                        "<Graph::send_size_request> A parent is required if no width is \
                               provided..."
                    );
                    return;
                }
            }
        };
        // This condition is to avoid having a graph with a bigger width than the window.
        if let Some(top) = self.area.get_toplevel() {
            let max_width = top.get_allocation().width;
            if width > max_width {
                width = max_width;
            }
        }
        self.area.set_size_request(
            if *self.display_labels.borrow() {
                width
                    - if width >= self.labels_layout_width {
                        self.labels_layout_width
                    } else {
                        width
                    }
            } else {
                width
            },
            200,
        );
    }
}

pub trait Connecter {
    fn connect_to_window_events(&self);
}

impl Connecter for Rc<RefCell<Graph>> {
    fn connect_to_window_events(&self) {
        let s = self.clone();
        if let Some(parent) = self.borrow().horizontal_layout.get_toplevel() {
            // TODO: ugly way to resize drawing area, I should find a better way
            parent.connect_configure_event(move |w, _| {
                let need_diff = s.borrow().initial_diff.is_none();
                if need_diff {
                    let mut s = s.borrow_mut();
                    let parent_width = if let Some(p) = s.area.get_parent() {
                        p.get_allocation().width
                    } else {
                        0
                    };
                    s.initial_diff = Some(w.get_allocation().width - parent_width);
                }
                s.borrow().send_size_request(None);
                false
            });
        } else {
            eprintln!("This method needs to be called *after* it has been put inside a window");
        }
    }
}
<commit_msg>Definitely fix graphs<commit_after>use cairo;
use gdk::{self, WindowExt};
use gtk::{self, BoxExt, ContainerExt, DrawingArea, ScrolledWindowExt, StateFlags, WidgetExt};
use std::cell::RefCell;

use std::rc::Rc;

use color::Color;
use utils::RotateVec;

const LEFT_WIDTH: f64 = 31.;

pub struct Graph {
    colors: Vec<Color>,
    pub data: Vec<RotateVec<f64>>,
    vertical_layout: gtk::Box,
    scroll_layout: gtk::ScrolledWindow,
    horizontal_layout: gtk::Box,
    pub area: DrawingArea,
    max: Option<RefCell<f64>>,
    keep_max: bool,
    display_labels: RefCell<bool>,
    initial_diff: Option<i32>,
    label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    labels_layout_width: i32,
    /// `minimum` is used only if `max` is set: it'll be the minimum that the `max` value will
    /// be able to go down.
    minimum: Option<f64>,
    // In %, from 0 to whatever
    overhead: Option<f64>,
}

impl Graph {
    /// If `max` is `None`, the graph will expect values between 0 and 1.
    ///
    /// If `keep_max` is set to `true`, then this value will never go down, meaning that graphs
    /// won't rescale down. It is not taken into account if `max` is `None`.
    pub fn new(max: Option<f64>, keep_max: bool) -> Graph {
        let g = Graph {
            colors: vec![],
            data: vec![],
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0),
            scroll_layout: gtk::ScrolledWindow::new(
                None::<&gtk::Adjustment>,
                None::<&gtk::Adjustment>,
            ),
            horizontal_layout: gtk::Box::new(gtk::Orientation::Horizontal, 0),
            area: DrawingArea::new(),
            max: if let Some(max) = max {
                Some(RefCell::new(max))
            } else {
                None
            },
            keep_max,
            display_labels: RefCell::new(true),
            initial_diff: None,
            label_callbacks: None,
            labels_layout_width: 80,
            minimum: None,
            overhead: None,
        };
        g.scroll_layout.set_min_content_width(g.labels_layout_width);
        g.scroll_layout.add(&g.vertical_layout);
        g.horizontal_layout.pack_start(&g.area, true, true, 0);
        g.horizontal_layout
            .pack_start(&g.scroll_layout, false, true, 10);
        g.horizontal_layout.set_margin_start(5);
        g
    }

    pub fn set_minimum(&mut self, minimum: Option<f64>) {
        self.minimum = minimum;
    }

    pub fn set_overhead(&mut self, overhead: Option<f64>) {
        if let Some(o) = overhead {
            assert!(o >= 0.);
        }
        self.overhead = overhead;
    }

    /// Changes the size of the layout containing labels (the one on the right).
    pub fn set_labels_width(&mut self, labels_layout_width: u32) {
        self.scroll_layout
            .set_min_content_width(labels_layout_width as i32);
        self.labels_layout_width = labels_layout_width as i32;
    }

    pub fn set_label_callbacks(
        &mut self,
        label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    ) {
        self.label_callbacks = label_callbacks;
    }

    pub fn set_display_labels(&self, display_labels: bool) {
        *self.display_labels.borrow_mut() = display_labels;
        if display_labels {
            self.scroll_layout.show_all();
        } else {
            self.scroll_layout.hide();
        }
        self.invalidate();
    }

    pub fn hide(&self) {
        self.horizontal_layout.hide();
    }

    pub fn show_all(&self) {
        self.horizontal_layout.show_all();
        if !*self.display_labels.borrow() {
            self.scroll_layout.hide();
        }
    }

    pub fn attach_to(&self, to: &gtk::Box) {
        to.add(&self.horizontal_layout);
    }

    pub fn push(&mut self, d: RotateVec<f64>, s: &str, override_color: Option<usize>) {
        let c = if let Some(over) = override_color {
            Color::generate(over)
        } else {
            Color::generate(self.data.len() + 11)
        };
        let l = gtk::Label::new(Some(s));
        l.override_color(
            StateFlags::from_bits(0).expect("from_bits failed"),
            Some(&c.to_gdk()),
        );
        self.vertical_layout.add(&l);
        self.colors.push(c);
        self.data.push(d);
    }

    fn draw_labels(&self, c: &cairo::Context, max: f64, height: f64) {
        if let Some(ref call) = self.label_callbacks {
            let entries = call(max);
            let font_size = 8.;

            c.set_source_rgb(0., 0., 0.);
            c.set_font_size(font_size);

            c.move_to(LEFT_WIDTH - 4. - entries[0].len() as f64 * 4., font_size);
            c.show_text(entries[0].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[1].len() as f64 * 4., height / 2.);
            c.show_text(entries[1].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[2].len() as f64 * 4., height - 2.);
            c.show_text(entries[2].as_str());

            c.move_to(
                font_size - 1.,
                height / 2. + 4. * (entries[3].len() >> 1) as f64,
            );
            c.rotate(-::std::f64::consts::FRAC_PI_2);
            c.show_text(entries[3].as_str());
        }
    }

    pub fn draw(&self, c: &cairo::Context, width: f64, height: f64) {
        let x_start = if self.label_callbacks.is_some() {
            LEFT_WIDTH
        } else {
            0.
        };

        // to limit line "fuzziness"
        #[inline]
        fn rounder(x: f64) -> f64 {
            let fract = x.fract();
            (if fract < 0.5 {
                x.trunc() + 0.5
            } else {
                x.trunc() + 1.5
            })
        }

        c.set_source_rgb(0., 0., 0.);
        c.rectangle(x_start, 0., width, height);
        c.fill();
        c.set_source_rgb(0.5, 0.5, 0.5);
        c.set_line_width(0.5);

        // We always draw 10 lines (12 if we count the borders).
        let x_step = (width - x_start) / 12.;
        let mut current = width - width / 12.;
        if x_step < 0.1 {
            c.stroke();
            return;
        }

        while current > x_start {
            c.move_to(rounder(current), 0.0);
            c.line_to(rounder(current), height);
            current -= x_step;
        }
        let step = height / 10.0;
        current = step - 1.0;
        while current < height - 1. {
            c.move_to(x_start, rounder(current));
            c.line_to(width, rounder(current));
            current += step;
        }
        c.stroke();

        c.set_line_width(1.);

        if let Some(ref self_max) = self.max {
            let mut max = if self.keep_max {
                *self_max.borrow()
            } else {
                1.
            };
            let len = self.data[0].len() - 1;
            for x in 0..len {
                for entry in &self.data {
                    if entry[x] > max {
                        max = entry[x];
                    }
                }
            }
            if let Some(min) = self.minimum {
                if min > max {
                    max = min;
                }
            } else if let Some(over) = self.overhead {
                max = max + max * over / 100.;
            }
            if !self.data.is_empty() && !self.data[0].is_empty() {
                let len = self.data[0].len() - 1;
                let step = (width - 2.0 - x_start) / len as f64;
                current = x_start + 1.0;
                let mut index = len;
                while current > x_start && index > 0 {
                    for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                        c.set_source_rgb(color.r, color.g, color.b);
                        c.move_to(
                            current + step,
                            height - entry[index - 1] / max * (height - 1.0),
                        );
                        c.line_to(current, height - entry[index] / max * (height - 1.0));
                        c.stroke();
                    }
                    current += step;
                    index -= 1;
                }
            }
            if max > *self_max.borrow() || !self.keep_max {
                *self_max.borrow_mut() = max;
            }
            self.draw_labels(c, max, height);
        } else if !self.data.is_empty() && !self.data[0].is_empty() {
            let len = self.data[0].len() - 1;
            let step = (width - 2.0 - x_start) / (len as f64);
            current = x_start + 1.0;
            let mut index = len;
            while current > x_start && index > 0 {
                for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                    c.set_source_rgb(color.r, color.g, color.b);
                    c.move_to(current + step, height - entry[index - 1] * (height - 1.0));
                    c.line_to(current, height - entry[index] * (height - 1.0));
                    c.stroke();
                }
                current += step;
                index -= 1;
            }
            // To be called in last to avoid having to restore state (rotation).
            self.draw_labels(c, 100., height);
        }
    }

    pub fn invalidate(&self) {
        if let Some(t_win) = self.area.get_window() {
            let (x, y) = self
                .area
                .translate_coordinates(&self.area, 0, 0)
                .expect("translate_coordinates failed");
            let rect = gdk::Rectangle {
                x,
                y,
                width: self.area.get_allocated_width(),
                height: self.area.get_allocated_height(),
            };
            t_win.invalidate_rect(Some(&rect), true);
        }
    }

    pub fn send_size_request(&self, width: Option<i32>) {
        let mut width = match width {
            Some(w) => w,
            None => {
                if let Some(parent) = self.area.get_parent() {
                    parent.get_allocation().width
                        - parent.get_margin_start()
                        - parent.get_margin_end()
                } else {
                    eprintln!(
                        "<Graph::send_size_request> A parent is required if no width is \
                               provided..."
                    );
                    return;
                }
            }
        };
        // This condition is to avoid having a graph with a bigger width than the window.
        if let Some(top) = self.area.get_toplevel() {
            let max_width = top.get_allocation().width;
            if width > max_width {
                width = max_width;
            }
        }
        self.area.set_size_request(
            if *self.display_labels.borrow() {
                width
                    - if width >= self.labels_layout_width {
                        self.labels_layout_width
                    } else {
                        width
                    }
            } else {
                width
            },
            200,
        );
    }
}

pub trait Connecter {
    fn connect_to_window_events(&self);
}

impl Connecter for Rc<RefCell<Graph>> {
    fn connect_to_window_events(&self) {
        let s = self.clone();
        if let Some(parent) = self.borrow().horizontal_layout.get_toplevel() {
            // TODO: ugly way to resize drawing area, I should find a better way
            parent.connect_configure_event(move |w, _| {
                let need_diff = s.borrow().initial_diff.is_none();
                if need_diff {
                    let mut s = s.borrow_mut();
                    let parent_width = if let Some(p) = s.area.get_parent() {
                        p.get_allocation().width
                    } else {
                        0
                    };
                    s.initial_diff = Some(w.get_allocation().width - parent_width);
                }
                s.borrow().send_size_request(None);
                false
            });
        } else {
            eprintln!("This method needs to be called *after* it has been put inside a window");
        }
    }
}
<|endoftext|>


<commit_before>use {Point3, Projective3};
use alga::general::SubsetOf;

/// A scan position image.
#[derive(Clone, Copy, Debug)]
pub struct Image {
    cop: Projective3,
    mounting_matrix: Projective3,
    sop: Projective3,
}

impl Image {
    /// Sets this image's mounting matrix.
    pub fn set_mounting_matrix<T>(&mut self, mounting_matrix: T)
        where T: SubsetOf<Projective3>
    {
        self.mounting_matrix = mounting_matrix.to_superset();
    }

    /// Sets this image's camera's own position matrix.
    pub fn set_cop<T>(&mut self, cop: T)
        where T: SubsetOf<Projective3>
    {
        self.cop = cop.to_superset();
    }

    /// Sets this image's scanner's own position matrix.
    pub fn set_sop<T>(&mut self, sop: T)
        where T: SubsetOf<Projective3>
    {
        self.sop = sop.to_superset();
    }

    /// Converts a point in the project's coordinate system to the camera's coordinate system.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::{Image, Point3};
    /// let image = Image::default();
    /// let input = Point3::new(1., 2., 3.);
    /// let output = image.prcs_to_cmcs(input);
    /// assert_eq!(output, input);
    /// ```
    pub fn prcs_to_cmcs(&self, point: Point3) -> Point3 {
        use alga::linear::Transformation;

        (self.mounting_matrix * self.cop.inverse() * self.sop.inverse()).transform_point(&point)
    }
}

impl Default for Image {
    fn default() -> Image {
        Image {
            cop: Projective3::identity(),
            mounting_matrix: Projective3::identity(),
            sop: Projective3::identity(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use nalgebra::Rotation3;
    use std::f64::consts::PI;

    #[test]
    fn prcs_to_cmcs_identity() {
        let image = Image::default();
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_eq!(output, input);
    }

    #[test]
    fn prcs_to_cmcs_mounting_matrix() {
        let mut image = Image::default();
        image.set_mounting_matrix(Rotation3::from_euler_angles(0., 0., PI / 2.));
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_relative_eq!(Point3::new(-2., 1., 3.), output);
    }

    #[test]
    fn prcs_to_cmcs_cop() {
        let mut image = Image::default();
        image.set_cop(Rotation3::from_euler_angles(0., 0., PI / 2.));
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_relative_eq!(Point3::new(2., -1., 3.), output);
    }

    #[test]
    fn prcs_to_cmcs_sop() {
        let mut image = Image::default();
        image.set_sop(Rotation3::from_euler_angles(0., 0., PI / 2.));
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_relative_eq!(Point3::new(2., -1., 3.), output);
    }
}
<commit_msg>Remove setters from image<commit_after>use {Point3, Projective3};

/// A scan position image.
#[derive(Clone, Copy, Debug)]
pub struct Image {
    cop: Projective3,
    mounting_matrix: Projective3,
    sop: Projective3,
}

impl Image {
    /// Converts a point in the project's coordinate system to the camera's coordinate system.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::{Image, Point3};
    /// let image = Image::default();
    /// let input = Point3::new(1., 2., 3.);
    /// let output = image.prcs_to_cmcs(input);
    /// assert_eq!(output, input);
    /// ```
    pub fn prcs_to_cmcs(&self, point: Point3) -> Point3 {
        use alga::linear::Transformation;

        (self.mounting_matrix * self.cop.inverse() * self.sop.inverse()).transform_point(&point)
    }
}

impl Default for Image {
    fn default() -> Image {
        Image {
            cop: Projective3::identity(),
            mounting_matrix: Projective3::identity(),
            sop: Projective3::identity(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use alga::general::SubsetOf;
    use nalgebra::Rotation3;
    use std::f64::consts::PI;

    #[test]
    fn prcs_to_cmcs_identity() {
        let image = Image::default();
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_eq!(output, input);
    }

    #[test]
    fn prcs_to_cmcs_mounting_matrix() {
        let mut image = Image::default();
        image.mounting_matrix = Rotation3::from_euler_angles(0., 0., PI / 2.).to_superset();
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_relative_eq!(Point3::new(-2., 1., 3.), output);
    }

    #[test]
    fn prcs_to_cmcs_cop() {
        let mut image = Image::default();
        image.cop = Rotation3::from_euler_angles(0., 0., PI / 2.).to_superset();
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_relative_eq!(Point3::new(2., -1., 3.), output);
    }

    #[test]
    fn prcs_to_cmcs_sop() {
        let mut image = Image::default();
        image.sop = Rotation3::from_euler_angles(0., 0., PI / 2.).to_superset();
        let input = Point3::new(1., 2., 3.);
        let output = image.prcs_to_cmcs(input);
        assert_relative_eq!(Point3::new(2., -1., 3.), output);
    }
}
<|endoftext|>


<commit_before>use std::any::TypeId;

use draw::RGB;
use devel::Poly;
use data::{IntPoint, FloatPoint, Empty};

use super::value_ptr::{ValuePtr, ValuePtrList, VoidPtr};
use super::compiler::FnRef;
use super::execute::Executor;

pub fn solid_fill(
   arguments: &[&ValuePtr],
   _: &Executor,
   _: &FnRef
) -> Vec<ValuePtr> {

   let list = value_ptr_as_ref!(arguments[0], ValuePtrList);

   let color = value_ptr_as_ref!(arguments[1], RGB);

   let mut points = Vec::new();

   if extract_poly_points(list, &mut points) {
      vecval!(
         Poly::new(points, color.clone())
      )
   } else {
      vecval!(Empty)
   }
}

fn extract_poly_points(list: &ValuePtrList, points: &mut Vec<Vec<IntPoint>>) -> bool {
   if list.len() == 0 {
      return true;
   }

   if TypeId::of::<ValuePtrList>() == list[0].type_id {
      for value_ptr in list.iter() {
         if TypeId::of::<ValuePtrList>() == value_ptr.type_id {
            let inner = value_ptr_as_ref!(value_ptr, ValuePtrList);

            if !extract_poly_points(inner, points) {
               return false;
            }
         } else {
            return false;
         }
      }
   } else {
      if list.len() < 3 {
         return false;
      }

      let mut inner_points: Vec<IntPoint> = Vec::new();

      for value_ptr in list.iter() {
         if TypeId::of::<IntPoint>() == value_ptr.type_id {
            let point = value_ptr_as_ref!(value_ptr, IntPoint);

            inner_points.push(point.clone());
         } else if TypeId::of::<IntPoint>() == value_ptr.type_id {
            let point = value_ptr_as_ref!(value_ptr, FloatPoint);

            inner_points.push(point.as_int());
         } else {
            return false;
         }
      }

      points.push(inner_points);
   }

   true
}

<commit_msg>Fill operator fix<commit_after>use std::any::TypeId;

use draw::RGB;
use devel::Poly;
use data::{IntPoint, FloatPoint, Empty};

use super::value_ptr::{ValuePtr, ValuePtrList, VoidPtr};
use super::compiler::FnRef;
use super::execute::Executor;

pub fn solid_fill(
   arguments: &[&ValuePtr],
   _: &Executor,
   _: &FnRef
) -> Vec<ValuePtr> {

   let list = value_ptr_as_ref!(arguments[0], ValuePtrList);

   let color = value_ptr_as_ref!(arguments[1], RGB);

   let mut points = Vec::new();

   if extract_poly_points(list, &mut points) {
      vecval!(
         Poly::new(points, color.clone())
      )
   } else {
      vecval!(Empty)
   }
}

fn extract_poly_points(list: &ValuePtrList, points: &mut Vec<Vec<IntPoint>>) -> bool {
   if list.len() == 0 {
      return true;
   }

   if TypeId::of::<ValuePtrList>() == list[0].type_id {
      for value_ptr in list.iter() {
         if TypeId::of::<ValuePtrList>() == value_ptr.type_id {
            let inner = value_ptr_as_ref!(value_ptr, ValuePtrList);

            if !extract_poly_points(inner, points) {
               return false;
            }
         } else {
            return false;
         }
      }
   } else {
      if list.len() < 3 {
         return false;
      }

      let mut inner_points: Vec<IntPoint> = Vec::new();

      for value_ptr in list.iter() {
         if TypeId::of::<IntPoint>() == value_ptr.type_id {
            let point = value_ptr_as_ref!(value_ptr, IntPoint);

            inner_points.push(point.clone());
         } else if TypeId::of::<FloatPoint>() == value_ptr.type_id {
            let point = value_ptr_as_ref!(value_ptr, FloatPoint);

            inner_points.push(point.as_int());
         } else {
            return false;
         }
      }

      points.push(inner_points);
   }

   true
}

<|endoftext|>


<commit_before>extern crate getopts;
extern crate rusqlite;
extern crate pibq;

use std::env;
use std::time::{Duration, Instant};
use getopts::Options;

use pibq::bluetherm;
use pibq::sql;
use pibq::models::{ConnectionStatus, Reading};

// interval between sending query packtets, in ms
const QUERY_INTERVAL: u64 = 7000;

// if no packets are returned, amount of time to wait before creating a timeout error, in ms
const TIMEOUT_INTERVAL: u64 = 15000;

const HEARTBEAT_INTERVAL: u64 = 1000;

struct Harvester {
    sql_conn: rusqlite::Connection,
    bt_conn: Option<bluetherm::Connection>,
    serial: String,
    disconnected: bool,
    disconnect_reason: Option<bluetherm::ConnectionEvent>,
    error_count: i64,
    send_interval: Duration,
    timeout_interval: Duration,
    last_send: Option<Instant>,
    last_receive: Option<Instant>
}

impl Harvester {
    fn new(sql_conn: rusqlite::Connection, serial: &str) -> Harvester {
        Harvester {
            sql_conn: sql_conn,
            bt_conn: Some(Harvester::connect_bluetherm(serial)),
            serial: serial.to_string(),
            disconnected: true,
            disconnect_reason: None,
            error_count: 0,
            send_interval: Duration::from_millis(QUERY_INTERVAL),
            timeout_interval: Duration::from_millis(TIMEOUT_INTERVAL),
            last_send: None,
            last_receive: None
        }
    }

    fn connect_bluetherm(serial: &str) -> bluetherm::Connection {
        bluetherm::Connection::new(serial, Some(HEARTBEAT_INTERVAL)).unwrap()
    }

    fn start(&mut self) {
        loop {
            match (self.last_send, self.last_receive) {
                (Some(sent), _) if sent.elapsed() < self.send_interval => {},
                //(Some(sent), Some(received)) if sent > received => {},
                _ => {
                    self.send_packet();
                    self.last_send = Some(Instant::now());
                }
            }

            let event = self.get_bt_conn().wait().unwrap();

            println!("{}", event);
            match event {
                bluetherm::ConnectionEvent::Packet(p) => {
                    self.record_packet(p);
                    self.last_send = Some(Instant::now());
                    self.last_receive = Some(Instant::now());
                },
                e @ bluetherm::ConnectionEvent::InvalidPacket(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::ReadError(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::WriteError(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::Heartbeat => {
                    match self.last_receive {
                        None => {
                            match self.last_send {
                                None => {},
                                Some(sent) => {
                                    if sent.elapsed() > self.timeout_interval {
                                        self.bt_error(e);
                                    }
                                }
                            }
                        },
                        Some(rec) => {
                            if rec.elapsed() > self.timeout_interval {
                                self.bt_error(e);
                            }
                        }
                    }
                }
            }
        }
    }

    fn get_bt_conn(&mut self) -> &mut bluetherm::Connection {
        self.bt_conn.as_mut().unwrap()
    }

    fn record_packet(&mut self, packet: bluetherm::Packet) {
        let mut reading = Reading::new();
        reading.value1 = packet.get_sensor1_reading();
        reading.value2 = packet.get_sensor2_reading();
        sql::insert_reading(&self.sql_conn, &mut reading).unwrap();
    }

    fn send_packet(&mut self) {
        println!("sending packet...");
        let p = bluetherm::Packet::temp_packet();
        match self.get_bt_conn().send(p) {
            Err(e) => {
                self.bt_error(bluetherm::ConnectionEvent::ReadError(e));
            },
            Ok(_) => {}
        }
        println!("done sending packet.");
    }

    fn bt_error(&mut self, evt: bluetherm::ConnectionEvent) {
        let mut report_error = true;

        if self.disconnected {
            match &self.disconnect_reason {
                &None => {},
                &Some(ref reason) => {
                    match (reason, &evt) {
                        (&bluetherm::ConnectionEvent::InvalidPacket(_), &bluetherm::ConnectionEvent::InvalidPacket(_)) |
                        (&bluetherm::ConnectionEvent::ReadError(_), &bluetherm::ConnectionEvent::ReadError(_)) |
                        (&bluetherm::ConnectionEvent::WriteError(_), &bluetherm::ConnectionEvent::WriteError(_)) |
                        (&bluetherm::ConnectionEvent::Heartbeat, &bluetherm::ConnectionEvent::Heartbeat) => {
                            report_error = false;
                        },
                        _ => {},
                    }
                }
            }
        }

        if report_error {
            let msg = match evt {
                bluetherm::ConnectionEvent::InvalidPacket(ref p) => { format!("Invalid Packet: [{}]", p) },
                bluetherm::ConnectionEvent::ReadError(ref err) => { format!("Read Error: {}", err) },
                bluetherm::ConnectionEvent::WriteError(ref err) => { format!("Write Error: {}", err) },
                bluetherm::ConnectionEvent::Heartbeat => { "Timeout".to_string() },
                _ => "Unknown Error".to_string()
            };

            let mut s = ConnectionStatus::new();
            s.is_disconnect = true;
            s.info = Some(msg);
            sql::insert_connection_status(&self.sql_conn, &mut s).unwrap();
        }

        println!("error: {}", evt);

        self.disconnected = true;
        self.disconnect_reason = Some(evt);

        self.error_count = self.error_count + 1;

        if self.error_count > 3 {
            self.error_count = 0;
            println!("killing old connection.");

            self.last_send = None;
            self.last_receive = None;

            let old = self.bt_conn.take();
            drop(old.unwrap());

            println!("old conneciton dropped; making new");

            self.bt_conn = Some(Harvester::connect_bluetherm(&self.serial));

            println!("new connection made");
        }
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.reqopt("s", "serial", "tty serial device", "DEV");
    opts.optopt("d", "dbfile", "sqlite DB file", "FILE");
    opts.optopt("m", "migrations", "migration folder", "DIR");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => {
            println!("{}", f.to_string());
            print_usage(&program, opts);
            return;
        }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let serial = matches.opt_str("s").unwrap();
    let dbfile = matches.opt_str("d").unwrap_or("pibq.sqlite".to_string());
    let migrations = matches.opt_str("m").unwrap_or("migrations".to_string());

    let db = sql::get_connection(&dbfile, Some(migrations)).unwrap();

    let mut h = Harvester::new(db, &serial);
    h.start();
}
<commit_msg>harvest working<commit_after>extern crate getopts;
extern crate rusqlite;
extern crate pibq;

use std::env;
use std::time::{Duration, Instant};
use getopts::Options;

use pibq::bluetherm;
use pibq::sql;
use pibq::models::{ConnectionStatus, Reading};

// interval between sending query packtets, in ms
const QUERY_INTERVAL: u64 = 7000;

// if no packets are returned, amount of time to wait before creating a timeout error, in ms
const TIMEOUT_INTERVAL: u64 = 15000;

const HEARTBEAT_INTERVAL: u64 = 1000;

struct Harvester {
    sql_conn: rusqlite::Connection,
    bt_conn: Option<bluetherm::Connection>,
    serial: String,
    disconnected: bool,
    disconnect_reason: Option<bluetherm::ConnectionEvent>,
    error_count: i64,
    send_interval: Duration,
    timeout_interval: Duration,
    last_send: Option<Instant>,
    last_receive: Option<Instant>
}

impl Harvester {
    fn new(sql_conn: rusqlite::Connection, serial: &str) -> Harvester {
        Harvester {
            sql_conn: sql_conn,
            bt_conn: Some(Harvester::connect_bluetherm(serial)),
            serial: serial.to_string(),
            disconnected: true,
            disconnect_reason: None,
            error_count: 0,
            send_interval: Duration::from_millis(QUERY_INTERVAL),
            timeout_interval: Duration::from_millis(TIMEOUT_INTERVAL),
            last_send: None,
            last_receive: None
        }
    }

    fn connect_bluetherm(serial: &str) -> bluetherm::Connection {
        bluetherm::Connection::new(serial, Some(HEARTBEAT_INTERVAL)).unwrap()
    }

    fn start(&mut self) {
        loop {
            match (self.last_send, self.last_receive) {
                (Some(sent), _) if sent.elapsed() < self.send_interval => {},
                //(Some(sent), Some(received)) if sent > received => {},
                _ => {
                    self.send_packet();
                    self.last_send = Some(Instant::now());
                }
            }

            let event = self.get_bt_conn().wait().unwrap();

            println!("{}", event);
            match event {
                bluetherm::ConnectionEvent::Packet(p) => {
                    self.record_packet(p);
                    self.last_send = Some(Instant::now());
                    self.last_receive = Some(Instant::now());
                    self.bt_success();
                },
                e @ bluetherm::ConnectionEvent::InvalidPacket(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::ReadError(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::WriteError(_) => { self.bt_error(e); },
                e @ bluetherm::ConnectionEvent::Heartbeat => {
                    match self.last_receive {
                        None => {
                            match self.last_send {
                                None => {},
                                Some(sent) => {
                                    if sent.elapsed() > self.timeout_interval {
                                        self.bt_error(e);
                                    }
                                }
                            }
                        },
                        Some(rec) => {
                            if rec.elapsed() > self.timeout_interval {
                                self.bt_error(e);
                            }
                        }
                    }
                }
            }
        }
    }

    fn get_bt_conn(&mut self) -> &mut bluetherm::Connection {
        self.bt_conn.as_mut().unwrap()
    }

    fn record_packet(&mut self, packet: bluetherm::Packet) {
        let mut reading = Reading::new();
        reading.value1 = packet.get_sensor1_reading();
        reading.value2 = packet.get_sensor2_reading();
        sql::insert_reading(&self.sql_conn, &mut reading).unwrap();
    }

    fn send_packet(&mut self) {
        let p = bluetherm::Packet::temp_packet();
        match self.get_bt_conn().send(p) {
            Err(e) => {
                self.bt_error(bluetherm::ConnectionEvent::ReadError(e));
            },
            Ok(_) => {}
        }
    }

    fn bt_success(&mut self) {
        if self.disconnected {
            let mut s = ConnectionStatus::new();
            s.is_connect = true;
            sql::insert_connection_status(&self.sql_conn, &mut s).unwrap();

            self.disconnected = false;
            self.disconnect_reason = None;
            self.error_count = 0;
        }
    }

    fn bt_error(&mut self, evt: bluetherm::ConnectionEvent) {
        let mut report_error = true;

        if self.disconnected {
            match &self.disconnect_reason {
                &None => {},
                &Some(ref reason) => {
                    match (reason, &evt) {
                        (&bluetherm::ConnectionEvent::InvalidPacket(_), &bluetherm::ConnectionEvent::InvalidPacket(_)) |
                        (&bluetherm::ConnectionEvent::ReadError(_), &bluetherm::ConnectionEvent::ReadError(_)) |
                        (&bluetherm::ConnectionEvent::WriteError(_), &bluetherm::ConnectionEvent::WriteError(_)) |
                        (&bluetherm::ConnectionEvent::Heartbeat, &bluetherm::ConnectionEvent::Heartbeat) => {
                            report_error = false;
                        },
                        _ => {},
                    }
                }
            }
        }

        if report_error {
            let msg = match evt {
                bluetherm::ConnectionEvent::InvalidPacket(ref p) => { format!("Invalid Packet: [{}]", p) },
                bluetherm::ConnectionEvent::ReadError(ref err) => { format!("Read Error: {}", err) },
                bluetherm::ConnectionEvent::WriteError(ref err) => { format!("Write Error: {}", err) },
                bluetherm::ConnectionEvent::Heartbeat => { "Timeout".to_string() },
                _ => "Unknown Error".to_string()
            };

            let mut s = ConnectionStatus::new();
            s.is_disconnect = true;
            s.info = Some(msg);
            sql::insert_connection_status(&self.sql_conn, &mut s).unwrap();
        }

        println!("error: {}", evt);

        self.disconnected = true;
        self.disconnect_reason = Some(evt);

        self.error_count = self.error_count + 1;

        if self.error_count > 3 {
            self.error_count = 0;
            println!("killing old connection.");

            self.last_send = None;
            self.last_receive = None;

            let old = self.bt_conn.take();
            drop(old.unwrap());

            println!("old conneciton dropped; making new");

            self.bt_conn = Some(Harvester::connect_bluetherm(&self.serial));

            println!("new connection made");
        }
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.reqopt("s", "serial", "tty serial device", "DEV");
    opts.optopt("d", "dbfile", "sqlite DB file", "FILE");
    opts.optopt("m", "migrations", "migration folder", "DIR");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => {
            println!("{}", f.to_string());
            print_usage(&program, opts);
            return;
        }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let serial = matches.opt_str("s").unwrap();
    let dbfile = matches.opt_str("d").unwrap_or("pibq.sqlite".to_string());
    let migrations = matches.opt_str("m").unwrap_or("migrations".to_string());

    let db = sql::get_connection(&dbfile, Some(migrations)).unwrap();

    let mut h = Harvester::new(db, &serial);
    h.start();
}
<|endoftext|>