CORE-POS/IS4C

View on GitHub
pos/is4c-nf/scale-drivers/drivers/NewMagellan/SPH_NCR_Scale.cs

Summary

Maintainability
F
3 days
Test Coverage
/*******************************************************************************

    Copyright 2010 Whole Foods Co-op

    This file is part of IT CORE.

    IT CORE is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    IT CORE is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    in the file license.txt along with IT CORE; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*********************************************************************************/

/**
Theoretical implementation. Assumes single-cable RS232 configuration with:
    STX byte: 0x2
    ETX byte: 0x3
    BCC byte: disabled
    PPD byte: disabled (aka PACESETTER)
*/

using System;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace SPH {

public class SPH_NCR_Scale : SerialPortHandler 
{

    enum WeighState { None, Motion, Over, Zero, NonZero, Under };
    private WeighState scale_state;
    private Object writeLock = new Object();
    private string last_weight;
    const byte STX = 0x2;
    const byte ETX = 0x3;

    public SPH_NCR_Scale(string p) : base(p)
    {
        sp = new SerialPort();
        sp.PortName = this.port;
        sp.BaudRate = 9600;
        sp.DataBits = 7;
        sp.StopBits = StopBits.One;
        sp.Parity = Parity.Odd;
        sp.RtsEnable = true;
        sp.Handshake = Handshake.None;
        sp.ReadTimeout = 500;
        
        scale_state = WeighState.None;
        last_weight = "0000";

        sp.Open();
    }

    override public void HandleMsg(string msg)
    {
        if (msg == "errorBeep") {
            Beeps(3);
        } else if (msg == "beepTwice") {
            Beeps(2);
        } else if (msg == "goodBeep") {
            Beeps(1);
        } else if (msg == "twoPairs") {
            Thread.Sleep(300);
            Beeps(2);
            Thread.Sleep(300);
            Beeps(2);
        } else if (msg == "rePoll") {
            /* ignore these commands on purpose
            scale_state = WeighState.None;
            GetStatus();
            */
        } else if (msg == "wakeup") {
            scale_state = WeighState.None;
            GetStatus();
        } else if (msg == "reBoot") {
            scale_state = WeighState.None;
            lock (writeLock) {
                // ASCII: 10
                sp.Write(new byte[]{ STX, 0x31, 0x30, ETX }, 0, 4);
                Thread.Sleep(5000);
                // ASCII: 14
                sp.Write(new byte[]{ STX, 0x31, 0x34, ETX }, 0, 4);
            }
        }
    }

    private void Beeps(int num)
    {
        lock (writeLock) {
            int count = 0;
            while(count < num) {
                // ASCII: 334
                sp.Write(new byte[]{ STX, 0x33, 0x33, 0x34, ETX }, 0, 5);
                Thread.Sleep(150);
                count++;
            }
        }
    }

    private void GetStatus()
    {
        lock (writeLock) {
            // ASCII: 14
            sp.Write(new byte[]{ STX, 0x31, 0x34, ETX }, 0, 4);
        }
    }

    override public void Read()
    {
        string buffer = "";
        if (this.verbose_mode > 0) {
            System.Console.WriteLine("Reading serial data");
        }
        GetStatus();
        while (SPH_Running) {
            try {
                int b = sp.ReadByte();
                if (b == ETX) {
                    // message complete
                    if (this.verbose_mode > 0) {
                        System.Console.WriteLine("RECV FROM SCALE: "+buffer);
                    }
                    buffer = this.ParseData(buffer);
                    if (buffer != null) {
                        if (this.verbose_mode > 0) {
                            System.Console.WriteLine("PASS TO POS: "+buffer);
                        }
                        this.PushOutput(buffer);
                    }
                    buffer = "";
                } else if (b == STX) {
                    // skip STX byte; converting to character doesn't really work
                } else {
                    buffer += ((char)b).ToString();
                }

            } catch {
                Thread.Sleep(100);
            }
        }
    }

    private void PushOutput(string s)
    {
        parent.MsgSend(s);
    }

    private string ParseData(string s)
    {
        if (s.Substring(0,1) == "0") { // scanner message
            if (s.Substring(0,3) == "08A" || s.Substring(0,3) == "08F") { // UPC-A or EAN-13
                return s.Substring(3);
            } else if (s.Substring(0,3) == "08E") { // UPC-E
                return this.ExpandUPCE(s.Substring(3));
            } else if (s.Substring(0,3) == "08R") { // GTIN / GS1
                return "GS1~"+s.Substring(2);
            } else if (s.Substring(0,4) == "08B1") { // Code39
                return s.Substring(4);
            } else if (s.Substring(0,4) == "08B2") { // Interleaved 2 of 5
                return s.Substring(4);
            } else if (s.Substring(0,4) == "08B3") { // Code128
                return s.Substring(4);
            } else {
                return s; // catch all
            }
        } else if (s.Substring(0,1) == "1") { // scale message
            /**
              The scale supports two primary commands:
              11 is "get stable weight". This tells the scale to return
              the next stable non-zero weight.
              14 is "get state". This tells the scale to return its
              current state.

              The "scale_state" variable tracks all six known scale states.
              The state is only changed if the status response is different
              than the current state and this only returns a non-null string
              when the state changes. The goal is to only pass a message back
              to POS once per state change. The "last_weight" is tracked in
              case the scale jumps directly from one stable, non-zero weight
              to another without passing through another state in between.
            */
            if (s.Substring(0,2) == "11") { // stable weight following weight request
                GetStatus();
                if (scale_state != WeighState.NonZero || last_weight != s.Substring(2)) {
                    scale_state = WeighState.NonZero;
                    last_weight = s.Substring(2);
                    return "S"+s;
                }
            } else if (s.Substring(0,3) == "140") { // scale not ready
                GetStatus();
                if (scale_state != WeighState.None) {
                    scale_state = WeighState.None;
                    return "S140";
                }
            } else if (s.Substring(0,3) == "141") { // weight not stable
                GetStatus();
                if (scale_state != WeighState.Motion) {
                    scale_state = WeighState.Motion;
                    return "S141";
                }
            } else if (s.Substring(0,3) == "142") { // weight over max
                GetStatus();
                if (scale_state != WeighState.Over) {
                    scale_state = WeighState.Over;
                    return "S142";
                }
            } else if (s.Substring(0,3) == "143") { // stable zero weight
                GetStatus();
                if (scale_state != WeighState.Zero) {
                    scale_state = WeighState.Zero;
                    return "S110000";
                }
            } else if (s.Substring(0,3) == "144") { // stable non-zero weight
                GetStatus();
                if (scale_state != WeighState.NonZero || last_weight != s.Substring(3)) {
                    scale_state = WeighState.NonZero;
                    last_weight = s.Substring(3);
                    return "S11"+s.Substring(3);
                }
            } else if (s.Substring(0,3) == "145") { // scale under zero weight
                GetStatus();
                if (scale_state != WeighState.Under) {
                    scale_state = WeighState.Under;
                    return "S145";
                }
            } else {
                GetStatus();
                return "S"+s; // catch all
            }
        } else { // not scanner or scale message
            return s; // catch all
        }

        return null;
    }

    private string ExpandUPCE(string upc)
    {
        string lead = upc.Substring(0,upc.Length-1);
        string tail = upc.Substring(upc.Length-1);

        if (tail == "0" || tail == "1" || tail == "2") {
            return lead.Substring(0,3)+tail+"0000"+lead.Substring(3);
        } else if (tail == "3") {
            return lead.Substring(0,4)+"00000"+lead.Substring(4);
        } else if (tail == "4") {
            return lead.Substring(0,5)+"00000"+lead.Substring(5);
        } else {
            return lead+"0000"+tail;
        }
    }
}

}