Files
tbf/Workplace/CheckItems/S640Stream.cs
T

317 lines
11 KiB
C#

///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Windows.Forms;
using log4net;
using SharedDatabase.Entities;
using System.Drawing;
namespace Workplace.CheckItems
{
public partial class S640Stream : UserControl, IDevice, ICheckItem
{
private static readonly ILog log = LogManager.GetLogger(typeof(S640Stream));
public override string ToString()
{
return string.Format("S640Stream: {0} {1}({2})", IxPolozky, Part.Name, string.IsNullOrEmpty(Part.OraDBType) ? string.Empty : Part.OraDBType);
}
///
/// Check item related variables
///
WorkplaceDlg parent;
public Part Part { get { return part; } }
Part part;
public int IxPolozky { get; set; }
public IList<string> BlackListedCodes { get; set; }
public string ScannedCode
{
get { return PcbNumber.ToString(); }
set { }
}
///
/// Opto serial and data stream related variables
///
SerialPort serialPort; /// Used in DebugMode.Normal
const int FramesBufferSize = 32; /// 8 sec @ 4 Hz
S640.DatastreamFrame[] optoData;
int optoDataIx;
string partOfTelegram; /// Data unprocessed by ReadDatastream(...), contains incomplete frame
Int64 currentRawVolumeUnwrapped;
Int64 currentRawTimeUnwrapped;
///
/// PCB Number and Radio address from data stream
///
public Int64 PcbNumber;
public Int64 RadioAddress;
///
IList<S640.PcbnrRadioaddrWeight> prwList;
Int64 lastPcbNumber;
Int64 lastNonzeroPcbNumber;
S640Stream()
{
InitializeComponent();
BlackListedCodes = new List<string>();
prwList = new List<S640.PcbnrRadioaddrWeight>();
}
/// <summary> Empty implementation, might be overriden in derived classes </summary>
public virtual void StartChangeHandler() { }
/// <summary> Empty implementation, might be overriden in derived classes </summary>
public virtual void StopChangeHandler() { }
public S640Stream(WorkplaceDlg parent, SharedDatabase.Entities.Part part)
: this()
{
this.parent = parent;
this.part = part;
kodMaterialuLabel.Text = part.Name;
descriptionMaterialuLabel.Text = part.Description;
}
void Localize() { }
public void Initialize()
{
currentRawVolumeUnwrapped = 0;
currentRawTimeUnwrapped = 0;
partOfTelegram = string.Empty;
/// Allocate memory for opto-data from iPerl
optoData = new S640.DatastreamFrame[FramesBufferSize];
for (int i = 0; i < FramesBufferSize; i++)
{
optoData[i] = new S640.DatastreamFrame { DateTime = DateTime.MinValue };
}
optoDataIx = 0;
/// Prepare serial port
string comPortName = string.Format("COM{0}", Program.LocalSettings.ScaleSerialPort);
serialPort = new SerialPort(comPortName, 9600, Parity.None, 8, StopBits.One);
serialPort.Handshake = Handshake.None;
serialPort.Open();
log.FatalFormat("{0} initialized: {1}", Name, this);
}
public void ClearCode() { }
public void SubmitCode(string code) { }
bool hasFocus;
bool sendLastPcbNr;
private void setFocusButton_Click(object sender, EventArgs e)
{
if (!hasFocus && !sendLastPcbNr && PcbNumber != 0 && PcbNumber != lastNonzeroPcbNumber)
{
lastNonzeroPcbNumber = PcbNumber;
sendLastPcbNr = true;
}
hasFocus = true;
setFocusButton.Text = ">";
parent.OnFocusPressed(this, new FocusPressedEventArgs(IxPolozky, Part.CodeLocation));
}
public void ResetFocus()
{
hasFocus = false;
setFocusButton.Text = "";
}
public void SetFocus()
{
if (!hasFocus && !sendLastPcbNr && PcbNumber != 0 && PcbNumber != lastNonzeroPcbNumber)
{
lastNonzeroPcbNumber = PcbNumber;
sendLastPcbNr = true;
}
hasFocus = true;
setFocusButton.Text = ">";
}
public void RunDeviceBefore()
{
lastPcbNumber = PcbNumber;
ReadDatastream();
ProcessBuffer_UpdatePcbNrAndRA();
pcbNumberTextBox.Text = (PcbNumber != 0) ? PcbNumber.ToString() : string.Empty; /// Always displayed
//if (sendLastPcbNr)
//{
// sendLastPcbNr = false;
// parent.OnBarcodeReceived(this, new BarcodeReceivedEventArgs(lastNonzeroPcbNumber.ToString(), IxPolozky,
// Part.CodeLocation, PartError.None));
//}
//
//if (hasFocus && PcbNumber != 0 && PcbNumber != lastNonzeroPcbNumber)
if (PcbNumber != 0 && PcbNumber != lastNonzeroPcbNumber)
{
lastNonzeroPcbNumber = PcbNumber;
parent.OnBarcodeReceived(this, new BarcodeReceivedEventArgs(PcbNumber.ToString(), IxPolozky,
Part.CodeLocation, PartError.None));
}
}
public void RunDeviceAfter() { }
public void StopDevice()
{
try
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
}
}
catch (Exception exc)
{
log.ErrorFormat("Closing serial port failed: {0}", exc.Message);
}
}
/// <summary>
/// 9600 Bd, 8 data bits, 1 stop bit, no parity
///
/// Telegram description:
///
/// AAAAAAAAAAAA[tab]BBBBBBBBBB[tab]CCCCCCCCC[tab]DDDDDDDDDD[tab]EE[cr][lf] (49 bytes)
///
/// Data Comment Type Calculate to decimal
/// ----------------------------------------------------------------
/// AAAAAAAAAAAA PCB number Decimal
/// BBBBBBBBBB Radio address Decimal
/// CCCCCCCCC Meter reading Decimal Volume in ml
/// DDDDDDDDDD Timestamp Decimal Time in ms
/// EE Checksum Hexadecimal
/// ----------------------------------------------------------------
///
/// Example:
/// 540210730770 4291524429 000250215 0433411876 56
/// 540210730770 4291524429 000250223 0433412126 4A
/// ...
/// </summary>
/// <param name="streamState">OptoState.Read or OptoState.Flush</param>
void ReadDatastream()
{
if (serialPort == null || !serialPort.IsOpen)
{
BackColor = Color.Red;
return;
}
BackColor = SystemColors.Control;
try
{
int receivedBytes = serialPort.BytesToRead;
if (receivedBytes > 0)
{
var buffer = new char[receivedBytes];
serialPort.Read(buffer, 0, receivedBytes);
string allRcvd = partOfTelegram + new string(buffer);
while (true)
{
int pos = allRcvd.IndexOf("\r\n");
if (pos < 0)
{
/// No CR+LF found =>stop processing => wait for more characters in the buffer in the next invocation
partOfTelegram = allRcvd;
return;
}
else if (pos < S640.DatastreamFrame.FrameLength - 2)
{
/// CR+LF found too early, truncate the beginning incl. CR+LF and keep processing datastream in this loop
allRcvd = allRcvd.Substring(pos + 2);
}
else
{
bool frameOk = optoData[optoDataIx].UpdateFromString(allRcvd.Substring(pos - S640.DatastreamFrame.FrameLength + 2, S640.DatastreamFrame.FrameLength),
ref currentRawVolumeUnwrapped, ref currentRawTimeUnwrapped);
if (frameOk)
{
/// Frame OK => Move to the next frame
optoDataIx++;
if (optoDataIx >= FramesBufferSize) optoDataIx = 0;
}
else
{
/// Invalid frame => Invalidate it (age it)
optoData[optoDataIx].DateTime = DateTime.MinValue;
}
allRcvd = allRcvd.Substring(pos + 2);
}
}
}
}
catch (Exception e)
{
log.FatalFormat("Opto-data serial port failure : {0}", e.Message);
if (e.InnerException != null)
{
log.FatalFormat("InnerMessage : {0}", e.InnerException.Message);
}
}
}
void ProcessBuffer_UpdatePcbNrAndRA()
{
DateTime now = DateTime.Now;
char[] pcbNrStr = new char[12];
char[] radioAddrStr = new char[10];
Int64 pcbNr;
Int64 radioAddr;
prwList.Clear();
for (int i = 0; i < FramesBufferSize; i++)
{
double age = (now - optoData[i].DateTime).TotalSeconds;
if (age < 5)
{
Array.Copy(optoData[i].Data, 0, pcbNrStr, 0, 12);
Array.Copy(optoData[i].Data, 12, radioAddrStr, 0, 10);
if (Int64.TryParse(new string(pcbNrStr), out pcbNr) && Int64.TryParse(new string(radioAddrStr), out radioAddr))
{
S640.PcbnrRadioaddrWeight.Accumulate(prwList, pcbNr, radioAddr, 5 - age);
}
}
}
if (prwList.Count > 0)
{
prwList = prwList.OrderByDescending(x => x.Weight).ToList();
PcbNumber = prwList[0].PcbNr;
RadioAddress = prwList[0].RadioAddr;
}
else
{
PcbNumber = 0;
RadioAddress = 0;
}
}
}
}