Tech Off Post

Single Post Permalink

View Thread: Observers leaking down to hardware devices
  • User profile image
    staceyw

    Hope this makes Eric smile a little. A question on TinyClr NETMF forum prompted this. The question was related to looking at a Cpu.Pin stream for a certain pattern. Right away I thought of Rx. However a 35K+ port of Rx is overkill. But mixing in some of the ideas, I was able to come up with two (very handy I think) extention methods that get the job done in a composable way.  You subscribe to an event pin with an observer and do what you need.  I thought it was cool.

    public delegate bool PinChange(bool state, DateTime time);
    public delegate bool PinChangeSpan(bool state, long tickSpan);
    public static class Extentions
    {
        /// <summary>
        /// This overload passes tick span between pin changes.
        /// </summary>
        public static void Subscribe(this InterruptPort port, PinChangeSpan predicate)
        {
            if (predicate == null) throw new ArgumentNullException("predicate");
            long lastTick = -1;
            port.Subscribe((bool state, DateTime dt) =>
                {
                    if (lastTick < 0)
                    {
                        lastTick = dt.Ticks;
                        // The first "real" pin change has no delta yet.
                        return predicate(state, -1);
                    }
                    long delta = dt.Ticks - lastTick;
                    lastTick = dt.Ticks;
                    return predicate(state, delta);
                }, 1); // Always skip first as dirty because no idea when it was set.
        }
    
        /// <summary>
        /// This overload passes DateTime of current pin state.
        /// </summary>
        /// <param name="predicate">User predicate.</param>
        /// <param name="skipCount">Number of changes to skip.</param>
        public static void Subscribe(this InterruptPort port, PinChange predicate, uint skipCount=0)
        {
            if (predicate == null) throw new ArgumentNullException("predicate");
            NativeEventHandler del = null;
            del = (uint data1, uint data2, DateTime time) =>
            {
                if (skipCount > 0)
                {
                    skipCount--;
                    return;
                }
    
                if (del == null)
                    return; // Already unregisted. Not sure why this needed to avoid extra callback. MF Bug?
    
                bool state = data2 == 0 ? false : true;
                // If user predicate not want more, then unregister.
                if (! predicate(state, time))
                {
                    port.OnInterrupt -= del;
                    del = null;
                }
            };
    
            // Register observer.
            port.OnInterrupt += del;
        }
    }
    

    Usage samples:

     
    InterruptPort inPort = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.Di13, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
    
    int highCount = 0; // local state var.
    inPort.Subscribe((isHigh, dt) =>
    {
        if (isHigh && ++highCount >= 5)
        {
            Debug.Print("Pin high 5 times. Do something.");
            return false; // unsubscribe.
        }
        return true; // keep subscription active.
    });
    
    // PulseIn sample:
    // Milliseconds span between first low and high.
    inPort.Subscribe((bool isHigh, long ticks) =>
    {
        if (ticks == -1)
            return true; // Skip first, because we have no delta time context yet. 
        if (isHigh)
        {
            Debug.Print("\nDelay between first low and first high: " + ticks / 10000);
            return false; // done.
        }
        else
            return true;
    });