/// <summary>
        /// Restituisce se la risorsa ha un attributo attivo che abilita
        /// la procedura di intestatura
        /// </summary>
        /// <param name="resource">Risorsa di cui valutare gli attributi</param>
        /// <param name="attributeName">Attributo da ricercare</param>
        /// <returns>Se l'attributo è presente, attivo e con valore 1</returns>
        public static bool HasActiveBooleanAttribute(this IMesResource resource, string attributeName)
        {
            if (resource == null ||
                resource.Settings.ResourceAttributes == null)
            {
                return(false);
            }

            var outResult = false;

            var isValid = resource.Settings.ResourceAttributes.TryGetActiveAttribute(attributeName, ValueContainerType.Boolean, out outResult);

            return(isValid && outResult);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Recupera le informazioni delle lavorazioni pianificate su Nicim
        /// per la macchina specificata
        /// NB: i dati sono generati dallo SCHEDULATORE di NICIM che è un modulo
        /// opzionale e solo pochi clienti lo hanno
        /// </summary>
        /// <param name="resource">Macchina per cui si vogliono le lavorazioni</param>
        /// <returns>Elenco lavorazioni pianificate per la risorsa</returns>
        private IList <NicimWorkPlanItem> GetResourceNicimWorkPlanItems(IMesResource resource)
        {
            var nicimRepeater = this.GetNicimRepeater();

            if (nicimRepeater == null || !nicimRepeater.DirectAccessEnabled)
            {
                return(new List <NicimWorkPlanItem>());
            }

            var workPlanItems = nicimRepeater.LoadWorkPlan(resource);

            return(workPlanItems == null
                       ? new List <NicimWorkPlanItem>()
                       : workPlanItems.ToList());
        }
        /// <summary>
        /// Verifica se è presente un attributo di tipo stringa e ne restituisce il valore
        /// </summary>
        /// <param name="resource">Risorsa di cui valutare gli attributi</param>
        /// <param name="attributeName">Attributo da ricercare</param>
        /// <param name="attributeValue">Valore estratto dall'attributo</param>
        /// <returns>Se l'attributo è presente e con un valore valido</returns>
        public static bool TryGetActiveResourceStringAttribute(this IMesResource resource, string attributeName,
                                                               out string attributeValue)
        {
            attributeValue = string.Empty;

            if (resource == null ||
                resource.Settings.ResourceAttributes == null)
            {
                return(false);
            }

            var isValid = resource.Settings
                          .ResourceAttributes
                          .TryGetActiveAttribute(attributeName, ValueContainerType.String, out attributeValue);

            return(isValid);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Recupera le informazioni delle lavorazioni in corso su Nicim
        /// per la macchina specificata
        /// </summary>
        /// <param name="resource">Macchina per cui si vogliono le lavorazioni</param>
        /// <returns>Elenco lavorazioni in corso per la risorsa</returns>
        private IList <NicimRunningOperation> GetResourceNicimRunningOperations(IMesResource resource)
        {
            var nicimRepeater = this.GetNicimRepeater();

            if (nicimRepeater == null || !nicimRepeater.DirectAccessEnabled)
            {
                return(new List <NicimRunningOperation>());
            }

            //operazioni attive sulla risorsa specificata
            var runningOperations = nicimRepeater.LoadRunningOperations(resource);

            ////operazioni attive su TUTTE le risorse
            //var allRunningOperations = nicimRepeater.LoadRunningOperations();

            //l'oggetto NicimRunningOperation ha i vari campi della vista Nicim
            //e una proprietà CustomFields che contiene l'elenco dei 10 campi
            //personalizzati

            return(runningOperations == null
                       ? new List <NicimRunningOperation>()
                       : runningOperations.ToList());
        }
Exemplo n.º 5
0
        /// <summary>
        /// Invio alla macchina specificata un ordine di produzione, se non in marcia
        /// </summary>
        private void WriteValuesOnPowerDevice(IMesResource resource, string orderValue, int qtyValue)
        {
            if (resource == null)
            {
                throw new ArgumentNullException(nameof(resource));
            }
            if (string.IsNullOrWhiteSpace(orderValue))
            {
                throw new ArgumentException("Value cannot be null or whitespace.", nameof(orderValue));
            }
            if (qtyValue <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(qtyValue));
            }

            var dvcService = this._MesManager.ServiceManager.GetService <IDvcIntegrationService>();

            if (dvcService == null || !dvcService.Enabled)
            {
                this._MesManager.ApplicationMainLogger.WriteMessage(MessageLevel.Info, false, LOGSOURCE,
                                                                    "WriteValuesOnPowerDevice(): PowerDevice integration not available");
                return;
            }

            const string dvcInstance = "localhost";

            const string qtyAddress            = @"{OPCV1}{TESTOPC}{storage.numeric.reg02}"; //quantità prevista
            const string orderAddress          = @"{OPCV1}{TESTOPC}{storage.string.reg01}";  //ordine di lavoro
            const string runningMachineAddress = @"{OPCV1}{TESTOPC}{storage.bool.reg01}";    //bool - macchina in marcia

            /*
             * prima di tutto leggo il valore da PowerDevice di macchina in marcia
             * se non lo trovo a false\zero interrompo l'operazione
             */
            var isRunningReadResult = dvcService.ReadAddressValue(runningMachineAddress,
                                                                  dvcInstance) as DvcReadOperationSuccess;

            if (isRunningReadResult?.AddressValue == null ||
                !isRunningReadResult.AddressValue.IsValid ||
                string.IsNullOrWhiteSpace(isRunningReadResult.AddressValue.ValueAsString))
            {
                this._MesManager.ApplicationMainLogger.WriteMessage(MessageLevel.Warning, false, LOGSOURCE,
                                                                    "WriteValuesOnPowerDevice(): cannot read if machine is running");
                return;
            }

            if (isRunningReadResult.AddressValue.ValueAsString.ToLowerInvariant() != Boolean.FalseString.ToLowerInvariant() &&
                isRunningReadResult.AddressValue.ValueAsString.ToLowerInvariant() != "0")
            {
                this._MesManager.ApplicationMainLogger.WriteMessage(MessageLevel.Warning, false, LOGSOURCE,
                                                                    "WriteValuesOnPowerDevice(): cannot write because machine is running");
                return;
            }

            /*
             * NB: i valori numerici devono essere convertiti in stringa,
             * se decimali il separatore è sempre il punto
             */
            var nfi = (NumberFormatInfo)CultureInfo.CurrentCulture.NumberFormat.Clone();

            nfi.NumberDecimalSeparator = ".";
            nfi.NumberGroupSeparator   = string.Empty;

            var qtyValueAsString = qtyValue.ToString(nfi);

            /*
             * procediamo alla scrittura
             */
            var orderWriteResponse = dvcService.SetAddressValue(orderAddress, dvcInstance, orderValue);
            var qtyWriteResponse   = dvcService.SetAddressValue(qtyAddress, dvcInstance, qtyValueAsString);

            if (orderWriteResponse == null || qtyWriteResponse == null)
            {
                this._MesManager.ApplicationMainLogger.WriteMessage(MessageLevel.Warning, false, LOGSOURCE,
                                                                    "WriteValuesOnPowerDevice(): null response from PowerDevice integration");
                return;
            }

            if (orderWriteResponse.Success && qtyWriteResponse.Success)
            {
                this._MesManager.ApplicationMainLogger.WriteMessage(MessageLevel.Info, false, LOGSOURCE,
                                                                    "WriteValuesOnPowerDevice(): write completed");

                return;
            }

            this._MesManager.ApplicationMainLogger.WriteMessage(MessageLevel.Warning, false, LOGSOURCE,
                                                                "WriteValuesOnPowerDevice(): bad response from PowerDevice integration! "
                                                                + "ORDER = {0} \\ {1}",
                                                                orderWriteResponse.Success,
                                                                qtyWriteResponse.Success);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Carica i dati di produzione per una pressofusione
        /// </summary>
        /// <param name="resource">Risorsa associata alla pressa</param>
        private void CheckMachineProduction(IMesResource resource)
        {
            /*
             * La tabella PRODUZ ha un Id e timestamp che usiamo per caricare solo
             * i nuovi record rispetto all'ultimo che abbiamo processato.
             * Le informazioni da passare da un ciclo all'altro vengono memorizzate in un
             * oggetto MachineProductionMemento sulla tabella SH97_RepositoryValues
             *
             */

            if (resource == null)
            {
                throw new ArgumentNullException(nameof(resource));
            }

            //abilitazione tramite attributo
            var enabled = resource.Settings
                          .ResourceAttributes
                          .GetActiveAttributeValueOrDefault <bool>(LocalConstants.RES_ATTR_DIECASTINGPROD_ENABLED, ValueContainerType.Boolean);

            if (!enabled)
            {
                this._MesLogger.WriteMessage(MessageLevel.Diagnostics, false, LOGRSOURCE,
                                             "CheckMachineProduction(): resource {0} not enabled", resource.Name);
                return;
            }
            //mi serve anche nome istanza SQL, anche in questo caso gestito tramite attributo.
            //Il nome del DB invece è cablato, mi aspetto che possa essere spostato, non rinominato
            var sqlInstanceName = resource.Settings
                                  .ResourceAttributes
                                  .GetActiveAttributeValueOrDefault <string>(LocalConstants.RES_ATTR_DIECASTINGPROD_SQL, ValueContainerType.String);

            if (string.IsNullOrWhiteSpace(sqlInstanceName))
            {
                this._MesLogger.WriteMessage(MessageLevel.Error, true, LOGRSOURCE,
                                             "CheckMachineProduction(): Bad SQL instance name resource {0}", resource.Name);
                return;
            }

            this._MesLogger.WriteMessage(MessageLevel.Diagnostics, false, LOGRSOURCE,
                                         "CheckMachineProduction(): start for resource {0}", resource.Name);


            var dbUserName = string.Empty; //TODO: inserire qui user e psw per accesso sql. in DieCastingDataAccessHub sono rpevisti dei default
            var dbPassword = string.Empty;

            var dataAccess = new DieCastingDataAccessHub(sqlInstanceName, dbUserName, dbPassword, this._MesLogger);

            if (!dataAccess.CheckConnection())
            {
                //db non raggiungibile
                this._MesManager.ApplicationMainLogger.WriteMessage(MessageLevel.Warning, true, LOGRSOURCE,
                                                                    "CheckMachineProduction(): DATABASE NOT AVAILABLE for {1} [{0}]",
                                                                    sqlInstanceName, resource.Name);
                return;
            }

            var localTime = DateTime.Now;
            //recupero la data del server SQL per gestire la differenza,
            //utile se l'istanza MS SQL è direttamente sul PC della macchina
            var machineTime = dataAccess.GetServerTime();

            if (machineTime == DateTime.MinValue)
            {
                machineTime = localTime;
            }

            var timeDifference = localTime - machineTime;

            var productionMemento = this.LoadResourceMemento(resource.Name);

            var productionStrokes = dataAccess.GetProduction(productionMemento.LastReadRecordNumber);

            var funnelEvents = new List <DataUnitEvent>();

            var notRunningAtLast = false;
            var lastTimestamp    = DateTime.Now;

            if (productionStrokes.Count > 0)
            {
                /*
                 * Cicliamo sui record acquisiti, ognuno dei quali rappresenta una battuta della pressa.
                 * Per ogni stampata creaiamo una Fine\Versamento, e subito dopo un inizio.
                 * Se l'ultimo record processato segnala che la macchina non era in automatico,
                 * creo una sospensione.
                 */
                foreach (var productionStroke in productionStrokes)
                {
                    var normalizedStrokeTime = productionStroke.Timestamp.Add(timeDifference);
                    if (normalizedStrokeTime < productionMemento.LastReadStrokeTimestamp)
                    {
                        this._MesLogger.WriteMessage(MessageLevel.Diagnostics, false, LOGRSOURCE,
                                                     "CheckMachineProduction(): stroke in the past for resource {0}",
                                                     resource.Name);
                        continue;
                    }

                    if (!productionStroke.MachineIsRunning)
                    {
                        notRunningAtLast = true;
                        lastTimestamp    = normalizedStrokeTime;

                        this._MesLogger.WriteMessage(MessageLevel.Warning, false, LOGRSOURCE,
                                                     "CheckMachineProduction(): stroke with resource {0} not running",
                                                     resource.Name);
                        continue;
                    }

                    notRunningAtLast = false;

                    this._MesLogger.WriteMessage(MessageLevel.Info, false, LOGRSOURCE,
                                                 "CheckMachineProduction(): NEW STROKE for resource {0} - {2} - {1}",
                                                 resource.Name, normalizedStrokeTime.ToString("G"),
                                                 productionStroke.MachineCycleNumber);

                    var bareQty     = 1; //un record per ogni stampata, contiamo le battute
                    var goodQty     = productionStroke.IsGood ? bareQty : 0;
                    var rejectedQty = !productionStroke.IsGood ? bareQty : 0;

                    //creo un done e poi uno start
                    var doneEvent = new ProductDoneEvent(resource.Name, normalizedStrokeTime.ToUniversalTime(),
                                                         productionStroke.Article,
                                                         goodQty, rejectedQty, 0,
                                                         productionStroke.MachineCycleNumber.ToString(), 0);
                    funnelEvents.Add(doneEvent);

                    var startEvent = new ArticleStartedEvent(resource.Name, normalizedStrokeTime.ToUniversalTime(),
                                                             productionStroke.Article, 0,
                                                             string.Empty,
                                                             0, string.Empty, 100,
                                                             productionStroke.Order ?? string.Empty);
                    funnelEvents.Add(startEvent);

                    //per definizione numero ciclo e timestamp sono maggiori del precedente
                    productionMemento.LastReadStrokeTimestamp = normalizedStrokeTime;
                    productionMemento.LastReadRecordNumber    = productionStroke.MachineCycleNumber;
                    productionMemento.LastReadStrokeId        = productionStroke.StrokeId;
                }
            }
            else
            {
                this._MesLogger.WriteMessage(MessageLevel.Diagnostics, false, LOGRSOURCE,
                                             "CheckMachineProduction(): no strokes for resource {0}", resource.Name);
            }

            if (notRunningAtLast && resource.Status.IsWorkingState())
            {
                //se l'ultimo record rilevato non ha macchina in automatico
                var suspension = new GenericSuspensionEvent(resource.Name, lastTimestamp.ToUniversalTime(), string.Empty);

                funnelEvents.Add(suspension);

                this._MesLogger.WriteMessage(MessageLevel.Info, true, LOGRSOURCE,
                                             "CheckMachineProduction(): setting suspension for resource NOT AUTO MODE {0} - {1}",
                                             resource.Name, lastTimestamp.ToString("G"));
            }

            //gli eventi di produzione creati vengono inseriti in un "imbuto"
            //che li mette su una coda di elaborazione asincrona
            if (funnelEvents.Count > 0)
            {
                this._MesManager.DataInputFunnel.EnqueueEvents(funnelEvents);
            }

            productionMemento.LastProcessingTime = DateTime.Now;

            //memorizza i dati di elaborazione
            this.SaveResourceMemento(resource.Name, productionMemento);
        }