317 lines
11 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|