using Logic.ProductionToProductMapper.Cordonel; using NLog; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO.Ports; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Xylem.Common.CommonCore.Configuration; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Hardware.WaterMeter.WaterMeterCore; using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts; using Xylem.Common.Logic.SoftwareAccessHelper; using Xylem.Common.Utils.Logging; namespace CordonelAssemblyLine.Model { internal class MarriageViewModel : INotifyPropertyChanged { public event EventHandler OnMsgRaise; public MarriageViewModel(string scannerPort, int slotNr) { _logger = NLogHelper.CreateOrGetLogger("ProductionUI"); ScannerPort = scannerPort; SlotNr = slotNr; } private readonly ILogger _logger; private string state = "Warte auf Scanner"; private string scanner = "keine Verbindung"; private string slot = "-"; private string result = "-"; private System.Windows.Media.Brush resultColor; public System.Windows.Media.Brush ResultColor { get { return resultColor; } set { resultColor = value; NotifyPropertyChanged(nameof(ResultColor)); } } public string State { get { return state; } set { state = value; NotifyPropertyChanged(nameof(State)); } } public string Scanner { get { return scanner; } set { scanner = value; NotifyPropertyChanged(nameof(Scanner)); } } public string Slot { get { return slot; } set { slot = value; NotifyPropertyChanged(nameof(Slot)); } } public string SerialNr { get { return serialNr; } set { serialNr = value; NotifyPropertyChanged(nameof(SerialNr)); } } public string Result { get { return result; } set { result = value; NotifyPropertyChanged(nameof(Result)); } } public bool CanMarry { get { return canMarry; } set { canMarry = value; NotifyPropertyChanged(nameof(CanMarry)); } } public string PcbId { get { return pcbId; } set { pcbId = value; NotifyPropertyChanged(nameof(PcbId)); } } private string pcbId; private bool canMarry = false; public event PropertyChangedEventHandler PropertyChanged; protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public void StartStore() { sm = PickingStateMachine.Store; } public void StartPicking(CancellationToken cancle) { var s = new Task(() => { PickingLoop(cancle); }, cancle); s.Start(); } public enum PickingStateMachine { Init, WaitForScanning, ScanningDone, WaitForDetect, GetMapping, Store, WaitForBuildingMeterReady, MappingError, Done } private PickingStateMachine sm = PickingStateMachine.Init; private class BarScanner : IDisposable { public bool? IsRuning = null; public String Input = ""; private String Port; private CancellationToken Cancellation; private Task WaitForScannerTask; public BarScanner(string port, CancellationToken cancellation) { Port = port; Cancellation = cancellation; } public void Start() { if (!IsRuning.HasValue || !IsRuning.Value) { if (Port == null) { return; } WaitForScannerTask = new Task(() => { try { using (var a = new SerialPort(Port)) { a.Open(); a.NewLine = "\r"; while (true) { if (Cancellation.IsCancellationRequested) { IsRuning = false; break; } else { IsRuning = true; Input = a.ReadLine(); a.Close(); IsRuning = false; break; } } } } catch (Exception ex) { IsRuning = false; } }, Cancellation); WaitForScannerTask.Start(); } } public void Dispose() { if (WaitForScannerTask.Status != TaskStatus.Faulted && WaitForScannerTask.Status != TaskStatus.RanToCompletion && WaitForScannerTask.Status != TaskStatus.Canceled) { WaitForScannerTask.Dispose(); } } } public bool Compare(Dictionary checkAppsIdVersion, uint Frequency, uint MeterSize, bool PressurePresent) { using (var Batch = new MeterBatch()) { using (var meter = new GenesisMeter()) { meter.SetupFromConfigFile(SlotNr); Batch.AddMeter(meter); State = "Am Zähler anmelden"; if ((bool)meter.Login()) { meter.SetProcessState(Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts.DisplayCodes.PickingStarted); var ret = RunCheck(checkAppsIdVersion, Frequency, MeterSize, PressurePresent, meter); if (ret) { meter.SetProcessState(Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts.DisplayCodes.PickingTested); var url = $"{ServiceUrls.MeterProcessStateUrl()}SetPickingState?PcbID={meter.PcbId}&FertigungsauftragsNr={serialNr}"; var RetString = LocalWebRequest.PostRequestAsyncAndGetContent(url); var result = false; if (bool.TryParse(RetString, out result)) { return result; } } else { meter.SetProcessState(Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts.DisplayCodes.PickingFailed); } } } } return false; } private bool RunCheck(Dictionary checkAppsIdVersion, uint Frequency, uint MeterSize, bool PressurePresent, GenesisMeter meter) { State = $"Üperprüfe Parameter"; var FrequencyReadOut = RegisterConverter.ConvertTo(meter.ReadRegister("SENSUSRADIO_FrequencyIndicator")); if (FrequencyReadOut != Frequency) { State = $"Falsche Frequenz! soll {Frequency}MHz ist {FrequencyReadOut}MHz"; return false; } var MeterSizeReadOut = RegisterConverter.ConvertTo(meter.ReadRegister("GENESISFLOW_MeterSize")); if (MeterSizeReadOut != MeterSize) { State = $"Meter Dn falsch! soll {MeterSize}DN ist {MeterSizeReadOut}DN"; return false; } var PressureReadOut = RegisterConverter.ConvertTo(meter.ReadRegister("METROLOGYASST_PressurePresent")); if (PressureReadOut != PressurePresent) { if (PressurePresent) { State = $"Pcb mit Drucksensor. Laut Auftrag keine verbaut"; } else { State = $"Pcb ohne Drucksensor. Laut Auftrag soll eine verbaut werden"; } //todo raus return false; } foreach (var item in checkAppsIdVersion) { try { var MeterFWApp = meter.MeterAppListVersion.First(a => item.Key == a.AppId); if (MeterFWApp == null || MeterFWApp.Version != item.Value) { State = $"Falsche FW, bitte Updaten"; return false; } } catch (Exception) { State = $"Fehler beim auslesen der FW"; return false; } } try { meter.WriteRegister("METROLOGYASST_StoreConfiguration", 1); Thread.Sleep(100); var storeRet = meter.ReadRegister("METROLOGYASST_StoreConfiguration"); if (!storeRet.All(a => a.Equals(0))) { State = $"Filesystem corrupt"; return false; } } catch (Exception) { State = $"Fehler beim überprüfen des Filesystems"; return false; } return true; } private PickingCompareItem OrderDetails; private BarScanner barScanner; private string serialNr; private readonly string ScannerPort; private readonly int SlotNr; //PickingState,PickingScanner,PickingSlot,PickingResult private void PickingLoop(CancellationToken token) { while (!token.IsCancellationRequested) { switch (sm) { case PickingStateMachine.Init: CanMarry = false; State = "Initialisierung"; Slot = $"Inaktiv EP {SlotNr}"; Scanner = $"Geschlossen {ScannerPort}"; //CheckAllPorts? Scanner = $"Öfffne {ScannerPort}"; //CheckAllPorts? barScanner = new BarScanner(ScannerPort, token); barScanner.Start(); while (!barScanner.IsRuning.HasValue) { Thread.Sleep(50); } if (barScanner.IsRuning.HasValue && barScanner.IsRuning.Value) { Scanner = $"Offen {ScannerPort}"; sm = PickingStateMachine.WaitForScanning; } else { Scanner = $"Nicht geöffnet {ScannerPort}"; sm = PickingStateMachine.Init; Thread.Sleep(5000); } break; case PickingStateMachine.WaitForScanning: OrderDetails = null; State = $"Warte auf Scannereingabe({ScannerPort})"; if (barScanner != null && !String.IsNullOrEmpty(barScanner.Input)) { SerialNr = barScanner.Input; Scanner = $"Geschlossen {ScannerPort}"; barScanner = null; sm = PickingStateMachine.WaitForDetect; } break; case PickingStateMachine.WaitForDetect: using (var Batch = new MeterBatch()) { using (var tryMeter = new GenesisMeter()) { try { State = $"Warte auf Pcb"; Slot = $"Öffne EP {SlotNr}"; tryMeter.SetupFromConfigFile(SlotNr); Batch.AddMeter(tryMeter); tryMeter.Logout(); Log("Try to get PcbID"); PcbId = tryMeter.GetPcbId(); var hasDetect = !string.IsNullOrEmpty(PcbId); if (hasDetect) { State = $"Pcb gefunden {PcbId}"; Log($"New PcbID detected {PcbId}"); sm = PickingStateMachine.GetMapping; } else { Slot = $"Keine Konnektivität EP {SlotNr}"; Log($"No Meter found"); } } catch (Exception e) { Log($"Error occur.{ Environment.NewLine} {e.Message}"); } } } break; case PickingStateMachine.GetMapping: State = $"Lade Auftragsdaten {serialNr}"; var retCode = 0; var str = LocalWebRequest.GetRequestWithError($"{ServiceUrls.MarriageServiceUrl()}GetPreMarriageState?SerialNumber={serialNr}&PcbId={PcbId}", out retCode, 15000); var retInt = MarriagePossibleEnum.None; if (retCode != 200 || !MarriagePossibleEnum.TryParse(str, out retInt)) { OnMsgRaise?.Invoke(this, $"Technisches Problem beim abfragen der Verfügbarkeit. Code ({retCode})"); sm = PickingStateMachine.MappingError; } List MappingText = new List(); if (retInt.HasFlag(MarriagePossibleEnum.NotPossibleDueAlredayMatch)) { MappingText.Add("Pcb ist schon mit dieser Seriennummer verbunden."); } if (retInt.HasFlag(MarriagePossibleEnum.NotPossibleDuePcblocked)) { MappingText.Add("Pcb ist schon mit einer anderen Seriennummer verbunden."); } if (retInt.HasFlag(MarriagePossibleEnum.NotPossibleDueSerialNumberBlocked)) { MappingText.Add("Seriennummer ist schon mit einer anderen Pcb verbunden."); } if (retInt.HasFlag(MarriagePossibleEnum.PcbNotMatchingOrder)) { MappingText.Add("Die Pcb ist noch keinem Auftrag zugeordnet."); } if (retInt.HasFlag(MarriagePossibleEnum.Error)) { MappingText.Add("Ein unbestimmter Fehler ist aufgetreten."); } if (MappingText.Any()) { var MsgText = ""; foreach (var item in MappingText) { MsgText = MsgText + Environment.NewLine + item; } OnMsgRaise?.Invoke(this, $"Diese Kombination ist nicht möglich.{MsgText}"); sm = PickingStateMachine.MappingError; Log($"No Meter found"); } else { sm = PickingStateMachine.WaitForBuildingMeterReady; } break; case PickingStateMachine.WaitForBuildingMeterReady: CanMarry = true; break; case PickingStateMachine.MappingError: State = $"Mapping nicht möglich, bitte erneut scannen"; Log($"Mapping nicht möglich, bitte erneut scannen"); Thread.Sleep(10000); sm = PickingStateMachine.Init; break; case PickingStateMachine.Store: CanMarry = false; var url = $"{ServiceUrls.MarriageServiceUrl()}SetPcbToSerial?PcbId={PcbId}&Scan={serialNr}&returnMetroData=0"; try { string resp = ""; try { resp = LocalWebRequest.GetRequest(url, 18000); } catch (Exception ex) { if (!ex.Message.Contains("Mapping already exist. check your marriage request")) { throw; } else { resp = "already store"; } } if (resp.Trim().ToLower().Contains("store")) { using (var Batch = new MeterBatch()) { using (var tryMeter = new GenesisMeter()) { try { State = $"Warte auf Pcb"; Slot = $"Öffne EP {SlotNr}"; tryMeter.SetupFromConfigFile(SlotNr); Batch.AddMeter(tryMeter); tryMeter.Logout(); Log("Try to get PcbID"); PcbId = tryMeter.GetPcbId(); var hasDetect = !string.IsNullOrEmpty(PcbId); if (hasDetect) { tryMeter.Login(); State = $"Pcb gefunden {PcbId}"; Log($"New PcbID detected {PcbId}"); tryMeter.SetProcessState(DisplayCodes.Mounted); tryMeter.PushProgress("AssemblyLine", Assembly.GetExecutingAssembly().GetName().Version.ToString()); sm = PickingStateMachine.Done; } else { Slot = $"Keine Konnektivität EP {SlotNr}"; Log($"No Meter found"); } OnMsgRaise?.Invoke(this, $"Zähler verheiratet. Kann zur Prüfstation"); } catch (Exception e) { Log($"Error occur.{ Environment.NewLine} {e.Message}"); } } } } else { OnMsgRaise?.Invoke(this, $"Fehler beim Speichern, bitte Support Kontaktieren.Response{resp}"); } sm = PickingStateMachine.Init; } catch (Exception ex) { if (ex is WebException) { Log($"Error on Store {url}: {Environment.NewLine} {((WebException)ex).Message}"); } else { Log(ex.Message); } } break; case PickingStateMachine.Done: OnMsgRaise?.Invoke(this, $"Bitte Display überprüfen. {Environment.NewLine}Sollte etwas anderes als {DisplayCodes.Mounted} im Display erscheinen ist der Zähler nicht erfolgreich bearbeitet."); sm = PickingStateMachine.Init; break; default: break; } Thread.Sleep(1000); } } public void Log(string txt) { _logger?.Info(txt); } } }