From 0f45d588e962b8230b9c139be75af6f078054dde Mon Sep 17 00:00:00 2001 From: Lucio Maranta Date: Wed, 5 Sep 2018 17:48:12 +0200 Subject: [PATCH] Fix Osai Tool Manager --- Libs/CMS_CORE_Library.dll | Bin 9081344 -> 9081344 bytes .../Controllers/NcToolManagerController.cs | 21 +- Step.NC/NcHandler.cs | 3818 ++++++++--------- .../WebApi/NcToolManagerController.cs | 11 +- Step/Listeners/SignalR/SignalRListener.cs | 4 +- 5 files changed, 1936 insertions(+), 1918 deletions(-) diff --git a/Libs/CMS_CORE_Library.dll b/Libs/CMS_CORE_Library.dll index 216f0b72e7601c57cb61ca76570d90bd12595b34..b187d9e925b82971173066771245e40a1987b929 100644 GIT binary patch delta 76372 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

Cm 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 delta 76545 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%0D_4A<9u3<(G_dljh%?J6=7ss{D@X6B5 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 XvGsFGX3TsN7!cGj=T4JR57qw%6@m x.Family).Load(); dbCtx.Entry(tool).Reference(x => x.Shank).Load(); + // GetFirstId(dbCtx.Tools.Select(x => x.ToolId).ToList()); + return tool; } @@ -355,9 +357,10 @@ namespace Step.Database.Controllers return dbFamily; } - public void DeleteFamily(DbNcFamilyModel family) + public void DeleteFamily(int famId) { - dbCtx.Families.Attach(family); + var family = FindFamily(famId); + dbCtx.Families.Remove(family); dbCtx.SaveChanges(); @@ -482,5 +485,17 @@ namespace Step.Database.Controllers dbCtx.SaveChanges(); } + + public int GetFirstId(IEnumerable objIds) + { + List listOfPossibleIds = Enumerable.Range(1, objIds.Max()).ToList(); + + IEnumerable res = listOfPossibleIds.Except(objIds).ToList(); + + if (res.Count() > 0) + return res.First(); + else + return 1; + } } } \ No newline at end of file diff --git a/Step.NC/NcHandler.cs b/Step.NC/NcHandler.cs index 667ecc43..3faf19b5 100644 --- a/Step.NC/NcHandler.cs +++ b/Step.NC/NcHandler.cs @@ -1,1913 +1,1913 @@ -using CMS_CORE; -using CMS_CORE.Demo; -using CMS_CORE.Fanuc; -using CMS_CORE.Osai; -using CMS_CORE.Siemens; -using Step.Database.Controllers; -using Step.Model.DatabaseModels; -using Step.Model.DTOModels; -using Step.Model.DTOModels.MaintenanceModels; -using Step.Model.DTOModels.ToolModels; -using Step.Utils; -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Globalization; -using System.IO; -using System.Linq; -using static CMS_CORE_Library.DataStructures; -using static Step.Config.ServerConfig; -using static Step.Model.Constants; - -namespace Step.NC -{ - public class NcHandler : IDisposable - { - public Nc numericalControl; - - public NcHandler() - { - // Choose NC - numericalControl = SetNumericalControl(); - } - - public void Dispose() - { - if(NcConfig.NcVendor != NC_VENDOR.SIEMENS) - numericalControl.NC_Disconnect(); - } - - public CmsError Connect() - { - // Connect NC - if (!numericalControl.NC_IsConnected()) - return numericalControl.NC_Connect(); - - return null; - } - - public Nc SetNumericalControl() - { - // Return new Numerical control instance choosed from the configuration - switch (NcConfig.NcVendor) - { - case NC_VENDOR.DEMO: - return new Nc_Demo(NcConfig.NcIpAddress, NcConfig.NcPort); - - case NC_VENDOR.FANUC: - return new Nc_Fanuc(NcConfig.NcIpAddress, NcConfig.NcPort, 2000); - - case NC_VENDOR.SIEMENS: - return new Nc_Siemens(2000); - - case NC_VENDOR.OSAI: - return new Nc_Osai(NcConfig.NcIpAddress, NcConfig.NcPort, 2000); - } - return null; - } - - #region File manager - - public CmsError GetFileList(string path, out List fileList) - { - fileList = new List(); - return numericalControl.FILES_RGetFileList(path, ref fileList); - } - - public CmsError GetFileInfo(string path, out InfoFile fileInfo) - { - fileInfo = new InfoFile(); - return numericalControl.FILES_RGetFileInfo(path, ref fileInfo); - } - - public CmsError GetActiveProgramInfo(out DTOActiveProgramDataModel dtoData) - { - dtoData = new DTOActiveProgramDataModel(); - // Get selectedProcess process id - ushort selectedProcess = 0; - CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); - if (cmsError.IsError()) - return cmsError; - - ActiveProgramDataModel data = new ActiveProgramDataModel(); - cmsError = numericalControl.FILES_RActiveProgramData(selectedProcess, ref data); - - dtoData = new DTOActiveProgramDataModel() - { - IsoLines = data.IsoLines, - Path = data.Path, - TimeLeft = data.TimeLeft - }; - - return cmsError; - } - - public CmsError SetActiveProgramInfo(string path, out DTOActiveProgramDataModel dtoData) - { - dtoData = new DTOActiveProgramDataModel(); - // Get selectedProcess process id - ushort selectedProcess = 0; - CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); - if (cmsError.IsError()) - return cmsError; - - ActiveProgramDataModel data = new ActiveProgramDataModel(); - cmsError = numericalControl.FILES_WSetActiveProgram(selectedProcess, path, ref data); - - dtoData = new DTOActiveProgramDataModel() - { - IsoLines = data.IsoLines, - Path = data.Path, - TimeLeft = data.TimeLeft - }; - - return cmsError; - } - - public CmsError DeactivateProgram(out DTOActiveProgramDataModel dtoData) - { - dtoData = new DTOActiveProgramDataModel(); - // Get selectedProcess process id - ushort selectedProcess = 0; - CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); - if (cmsError.IsError()) - return cmsError; - - ActiveProgramDataModel data = new ActiveProgramDataModel(); - cmsError = numericalControl.FILES_WDeactivateProgram(selectedProcess); - - dtoData = new DTOActiveProgramDataModel() - { - IsoLines = data.IsoLines, - Path = data.Path, - TimeLeft = data.TimeLeft - }; - - return cmsError; - } - - public CmsError UploadPartProgramAndActivate(string localPath, string fileName, out ActiveProgramDataModel programData) - { - programData = new ActiveProgramDataModel(); - // Upload to NC - string newFilePath = ""; - CmsError cmsError = numericalControl.FILES_UploadPartProgram(localPath, fileName, ref newFilePath); - if (cmsError.IsError()) - return cmsError; - - // Delete local file - File.Delete(localPath + "\\" + fileName); - - // Get selectedProcess id - ushort selectedProcess = 0; - cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); - if (cmsError.IsError()) - return cmsError; - // Activate - return numericalControl.FILES_WSetActiveProgram(selectedProcess, newFilePath, ref programData); - } - - #endregion File manager - - #region Read Data - - public CmsError GetNcGenericData(out DTONcGenericDataModel genericData) - { - genericData = new DTONcGenericDataModel(MachineConfig.Model); - - // Get date time - DateTime dateTime = new DateTime(); - CmsError cmsError = numericalControl.NC_RDateTime(ref dateTime); - if (cmsError.errorCode != 0) - return cmsError; - - genericData.DateTime = dateTime; - - // Get language - CultureInfo lang = new CultureInfo("en"); - cmsError = numericalControl.NC_RLanguage(ref lang); - if (cmsError.errorCode != 0) - return cmsError; - - genericData.Language = lang.TwoLetterISOLanguageName; - - string tmpInfo = ""; - // Get serial number - cmsError = numericalControl.NC_RSerialNumber(ref tmpInfo); - if (cmsError.errorCode != 0) - return cmsError; - - genericData.SerialNumber = tmpInfo; - - // Get software version - numericalControl.NC_RSoftwareVersion(ref tmpInfo); - if (cmsError.errorCode != 0) - return cmsError; - - genericData.NcSoftwareVersion = tmpInfo; - - // Get model name - numericalControl.NC_RModelName(ref tmpInfo); - if (cmsError.errorCode != 0) - return cmsError; - - genericData.NcModel = tmpInfo; - - // Get machine number - numericalControl.NC_RMachineNumber(ref tmpInfo); - if (cmsError.errorCode != 0) - return cmsError; - - genericData.CmsMachineIdNumber = tmpInfo; - - // Get max process number - ushort procNum = 0; - numericalControl.NC_RProcessesNum(ref procNum); - if (cmsError.errorCode != 0) - return cmsError; - // Max process number - genericData.ProcessNumber = procNum; - // Get Installation Date - genericData.InstallationDate = DateTime.Now; - // Get PLC version - genericData.PlcVersion = "1.0.0"; - // Get PLC version - genericData.UnitOfMeasurement = "mm"; - - return cmsError; - } - - public CmsError GetNcAlarms(out DTOAlarmsModel alarms) - { - alarms = new DTOAlarmsModel(); - List tmpAlarms = new List(); - - // Read NC active alarms - CmsError cmsError = numericalControl.NC_RActiveAlarms(ref tmpAlarms); - if (cmsError.errorCode != 0) - return cmsError; - - // Create response list from NC data - foreach (AlarmModel ncAlarm in tmpAlarms) - { - alarms.NcAlarms.Add(new GenericAlarmModel() - { - Id = ncAlarm.Id, - Message = ncAlarm.Message, - DateTime = DateTime.Now, - RestorationIsEnabled = false - }); - } - // Select distints - //alarms.NcAlarms = alarms.NcAlarms?.GroupBy(x => x.Id).Select(x => x.First()).ToList(); - - // Get NC max process number - ushort maxProcNumber = 0; - cmsError = numericalControl.NC_RProcessesNum(ref maxProcNumber); - if (cmsError.errorCode != 0) - return cmsError; - - // For each process - for (ushort i = 1; i <= maxProcNumber; i++) - { - // Get process active alarms - numericalControl.PROC_RActiveAlarms(i, ref tmpAlarms); - // Create response list from NC data - foreach (AlarmModel processAlarm in tmpAlarms) - { - alarms.ProcessAlarms.Add(new ProcessAlarmModel() - { - Id = processAlarm.Id, - Message = processAlarm.Message, - Process = processAlarm.Process, - DateTime = DateTime.Now, - RestorationIsEnabled = false - }); - } - } - - List plcAlarms = new List(); - // Read PLC Active Messages - cmsError = numericalControl.PLC_RActiveMessages(ref plcAlarms); - bool restorationIsEnabled = false; - foreach (PlcAlarmModel plcAlarm in plcAlarms) - { - var configuredAlarm = InitialAlarmsConfig.Where(x => x.PlcId == plcAlarm.Id).FirstOrDefault(); - if (configuredAlarm == null) - restorationIsEnabled = false; - else - restorationIsEnabled = configuredAlarm.RestoreIsActive; - - // Create response list from plc data - alarms.PlcAlarms.Add(new DTOPlcAlarmModel() - { - Id = plcAlarm.Id, - IsWarning = plcAlarm.IsWarning, - Process = plcAlarm.Process, - RestorationIsActive = plcAlarm.RestorationIsActive, - DateTime = DateTime.Now, - RestorationIsEnabled = restorationIsEnabled - }); - } - if (cmsError.errorCode != 0) - return cmsError; - - return cmsError; - } - - public CmsError GetAxesPositions(out List axes) - { - axes = new List(); - - // Get NC max process number - ushort maxProcNumber = 0; - CmsError cmsError = numericalControl.NC_RProcessesNum(ref maxProcNumber); - if (cmsError.errorCode != 0) - return cmsError; - - // For each process - for (ushort i = 1; i <= maxProcNumber; i++) - { - cmsError = GetAxesPositionsByProcess(i, out DTOAxesModel axis); - if (cmsError.errorCode != 0) - return cmsError; - - axes.Add(axis); - } - - return cmsError; - } - - public CmsError GetAxesPositionsBySelectedProcess(out DTOAxesModel axes) - { - axes = new DTOAxesModel(); - - // Get selectedProcess process number - ushort selectedProcess = 0; - CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); - if (cmsError.IsError()) - return cmsError; - - if (selectedProcess > 0) - cmsError = GetAxesPositionsByProcess(selectedProcess, out axes); - - return cmsError; - } - - public CmsError GetAxesPositionsByProcess(ushort processNum, out DTOAxesModel axes) - { - axes = new DTOAxesModel(); - - CmsError cmsError = numericalControl.AXES_RInterpPosition(processNum, ref axes.interpolated); - if (cmsError.errorCode != 0) - return cmsError; - - cmsError = numericalControl.AXES_RMachinePosition(processNum, ref axes.machine); - if (cmsError.errorCode != 0) - return cmsError; - - cmsError = numericalControl.AXES_RProgrPosition(processNum, ref axes.programmePos); - if (cmsError.errorCode != 0) - return cmsError; - - cmsError = numericalControl.AXES_RDistanceToGo(processNum, ref axes.toGo); - if (cmsError.errorCode != 0) - return cmsError; - - //numericalControl.AXES_RFollowingError(1, ref axes.followingErr); - - return cmsError; - } - - public CmsError GetPowerOnData(out DTOPowerOnDataModel resultPowerOnData) - { - resultPowerOnData = new DTOPowerOnDataModel(); - - // Init object - PreAndPostPowerOnModel prePostPowerOn = new PreAndPostPowerOnModel - { - PostPowerOn = new PostPowerOnModel(), - PrePowerOn = new PrePowerOnModel() - }; - - // Read pre and post power on data - CmsError cmsError = numericalControl.PLC_RPowerOnData(ref prePostPowerOn); - if (cmsError.IsError()) - return cmsError; - - // Set return object - resultPowerOnData.PrePowerOn = prePostPowerOn.PrePowerOn; - resultPowerOnData.PostPowerOn = prePostPowerOn.PostPowerOn; - - // Read axes procedure data from - cmsError = numericalControl.PLC_RAxesResetData(ref resultPowerOnData.AxisReset); - - return cmsError; - } - - public CmsError GetProcessesData(out DTOProcessesDataModel processesData) - { - processesData = new DTOProcessesDataModel(); - - // Get NC max process number - ushort maxProcNumber = 0; - CmsError cmsError = numericalControl.NC_RProcessesNum(ref maxProcNumber); - if (cmsError.errorCode != 0) - return cmsError; - - PROC_STATUS status = PROC_STATUS.IDLE; - // For each process get info - for (ushort i = 1; i <= maxProcNumber; i++) - { - ProcessDataModel tmpProcPP = new ProcessDataModel(); - // Get process status - cmsError = numericalControl.PROC_RStatusAndData(i, ref tmpProcPP); - // if at least one process is in run, NC is running - // Add to list - processesData.processes.Add(new ProcessModel() - { - Id = tmpProcPP.Id, - IsInAlarm = tmpProcPP.IsInAlarm, - PartProgramName = tmpProcPP.PartProgramName, - Reps = tmpProcPP.Reps, - Status = tmpProcPP.Status, - Type = tmpProcPP.Type, - Visible = tmpProcPP.Visible - }); - - if (tmpProcPP.IsSelected && processesData.selectedProcess == 0) // TODO remove with multi-process - processesData.selectedProcess = (ushort)tmpProcPP.Id; - - // Check if there are running process - if (!processesData.isRunning) - { - // Get process status - cmsError = numericalControl.PROC_RStatus(i, ref status); - if (status == PROC_STATUS.RUN || status == PROC_STATUS.HOLD) - processesData.isRunning = true; - else - status = PROC_STATUS.IDLE; - } - } - // Read selected axes - cmsError = numericalControl.AXES_RSelectedAxis(ref processesData.selectedAxis); - - return cmsError; - } - - public CmsError GetFunctionsMappedWithNC(out List functionsAccessList) - { - functionsAccessList = new List(); - // Read plc functionality - List functionalityList = null; - CmsError cmsError = numericalControl.PLC_RFunctionAccess(ref functionalityList); - - // If empty return - if (functionalityList == null || functionalityList.Count == 0) - return cmsError; - - using (FunctionsAccessController functionsAccessController = new FunctionsAccessController()) - { - // Map plc and database data - functionsAccessList = functionsAccessController.GetFunctionsMappedWithPlc(functionalityList); - } - - return cmsError; - } - - public CmsError GetMainenancesCounter(out List counters) - { - // List of PLC counters - counters = new List(); - // Get counters values from PLC - CmsError cmsError = numericalControl.PLC_RMachineCounters(ref counters); - - return cmsError; - } - - public CmsError GetMaintenanceData(out DTOMaintenanceModel dTOMaintenance, int maintenanceId, int userId) - { - dTOMaintenance = null; - - CmsError cmsError = GetMaintenances(out List maintenance, userId); - if (cmsError.IsError()) - return cmsError; - - dTOMaintenance = maintenance.Where(x => x.Id == maintenanceId).FirstOrDefault(); - - return cmsError; - } - - public CmsError GetMaintenances(out List dtoMaintenancesModel, int userId) - { - // Return value - dtoMaintenancesModel = new List(); - - // Get counters values from PLC - CmsError cmsError = GetMainenancesCounter(out List counters); - if (cmsError.IsError()) - return cmsError; - - using (MaintenancesController maintenancesController = new MaintenancesController()) - { - // Get the last performed maintenance for each maintenance - List performedMaintenances = maintenancesController.FindLastPerformedMaintenances(); - - // Get all the active maintenances - List maintenances = maintenancesController.FindAll(); // TODO db or config file? - - double percentage = 0; - TimeSpan missingDays = new TimeSpan(); - CounterModel counter = new CounterModel(); - - bool canEdit = false; - bool canPerform = false; - foreach (var currMaintenance in maintenances) - { - // Get matching last performed maintenance for current maintenance - PerformedMaintenanceModel performed = performedMaintenances.Find(x => x.MaintenanceId == currMaintenance.MaintenanceId); - - switch (currMaintenance.Type) - { - case MAINTENANCE_TYPE.MACHINE_INTERVAL: - { - // Get matching counter for the current maintenance - counter = counters.Find(x => x.Id == currMaintenance.CounterId); - - int perfVal = 0; - if (performed != null) - perfVal = performed.CounterValue; - // Calc percentage = PLC - PERFORMED VALUE * 100 / interval - percentage = ((counter.Value - perfVal) * 100) / SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value); - missingDays = TimeSpan.FromMinutes(-1 * (counter.Value - perfVal - SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value))); - } - break; - - case MAINTENANCE_TYPE.EXP_DATE: - { - // Already performed - if (performed != null) - { - percentage = 100; - missingDays = TimeSpan.Zero; - } - else - { - percentage = ((DateTime.Now - currMaintenance.CreationDate).TotalMinutes * 100) / (currMaintenance.Deadline - currMaintenance.CreationDate).TotalMinutes; // TimeSpan.FromTicks((currMaintenance.Deadline.Ticks)).TotalMinutes - missingDays = currMaintenance.Deadline - DateTime.Now; - counter = new CounterModel(); - } - } - break; - - case MAINTENANCE_TYPE.TIME_INTERVAL: - { - // Get last performed, default value is maintenance creation date - DateTime perfDate = currMaintenance.CreationDate; - if (performed != null) - perfDate = performed.Date; - - // Now - last performed = elapsed interval since last maintenance - TimeSpan timePassedFromLastMaint = DateTime.Now.Subtract(perfDate); - // Convert in minutes - double minutesPassedFromLastMaint = timePassedFromLastMaint.TotalMinutes; - - // percentage = Passed minutes since last maintenance * 100 / Interval - percentage = (minutesPassedFromLastMaint * 100) / SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value); - missingDays = TimeSpan.FromMinutes(-1 * (minutesPassedFromLastMaint - SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value))); - counter = new CounterModel(); - } - break; - } - - canEdit = false; - using (MachinesUsersController machineUsersController = new MachinesUsersController()) - { - if (currMaintenance.UserId != null) - { - // Check if user can edit the maintenance -> caller id - maintenance user id - int comparision = machineUsersController.CompareUsersRole(userId, currMaintenance.UserId.Value, MachineConfig.MachineId); - if (comparision >= 0) - canPerform = canEdit = true; - } - else - { - canEdit = false; - canPerform = machineUsersController.UserIsCmsAdmin(MachineConfig.MachineId, userId); - } - } - - dtoMaintenancesModel.Add(new DTOMaintenanceModel() - { - Id = currMaintenance.MaintenanceId, - Title = currMaintenance.UserId == null ? "" : currMaintenance.Title, - CreationDate = currMaintenance.CreationDate, - Deadline = currMaintenance.Deadline, - Description = currMaintenance.Description, - Interval = currMaintenance.Interval, - LastExpirationDate = currMaintenance.LastExpirationDate, - LastPerformedDate = performed?.Date, - Type = currMaintenance.Type.ToString(), - UnitOfMeasure = currMaintenance.UnitOfMeasure.ToString(), - PercentageOfCompletion = percentage >= 0 && percentage < 100 ? Convert.ToInt32(percentage) : 100, - MissingDays = missingDays, - PlcCounter = counter.Value, - CreatedByCms = currMaintenance.UserId == null, - CanEdit = canEdit, - CanPerform = canPerform - }); - } - } - - return cmsError; - } - - public CmsError GetExpiredMaintenances(out List expiredMaintenance) - { - // Return value - expiredMaintenance = new List(); - - // Get counters values from PLC - CmsError cmsError = GetMainenancesCounter(out List counters); - if (cmsError.IsError()) - return cmsError; - - using (MaintenancesController maintenancesController = new MaintenancesController()) - { - // Get the last performed maintenance for each maintenance - List performedMaintenances = maintenancesController.FindLastPerformedMaintenances(); - - // Get all the active maintenances - List maintenances = maintenancesController.FindAll(); // TODO db or config file? - - foreach (var currMaintenance in maintenances) - { - // Get matching last performed maintenance for current maintenance - var performed = performedMaintenances.Find(x => x.MaintenanceId == currMaintenance.MaintenanceId); - - switch (currMaintenance.Type) - { - case MAINTENANCE_TYPE.MACHINE_INTERVAL: - { - // Get matching counter for the current maintenance - var counter = counters.Find(x => x.Id == currMaintenance.CounterId); - - int perfVal = 0; - if (performed != null) - perfVal = performed.CounterValue; - - // MAINTENANCE_INTERVAL <= PLC - LASTPERFORMED - if (SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value) <= counter.Value - perfVal) - { - // Update last expiration date if null - if (currMaintenance.LastExpirationDate == null) - { - maintenancesController.UpdateLastExpirationDate(currMaintenance.MaintenanceId, DateTime.Now); - currMaintenance.LastExpirationDate = DateTime.Now; - } - - // Add item to return list - expiredMaintenance.Add(new DTOExpiredMaintenanceModel() - { - Id = currMaintenance.MaintenanceId, - ExpirationDate = (DateTime)currMaintenance.LastExpirationDate, - CreatedByCms = currMaintenance.UserId == null, - Title = currMaintenance.UserId == null ? "" : currMaintenance.Title - }); - } - } - break; - - case MAINTENANCE_TYPE.EXP_DATE: - { - // Already performed - if (performed != null) - break; - // DEADLINE <= NOW - if (currMaintenance.Deadline <= DateTime.Now) - { - maintenancesController.UpdateLastExpirationDate(currMaintenance.MaintenanceId, currMaintenance.Deadline); - currMaintenance.LastExpirationDate = currMaintenance.Deadline; - - // Add item to return list - expiredMaintenance.Add(new DTOExpiredMaintenanceModel() - { - Id = currMaintenance.MaintenanceId, - ExpirationDate = currMaintenance.Deadline, - CreatedByCms = currMaintenance.UserId == null, - Title = currMaintenance.UserId == null ? "" : currMaintenance.Title, - }); - } - } - break; - - case MAINTENANCE_TYPE.TIME_INTERVAL: - { - // Get last performed, default value is maintenance creation date - DateTime perfDate = currMaintenance.CreationDate; - if (performed != null) - perfDate = performed.Date; - - // Now - last performed = elapsed interval since last maintenance - TimeSpan timePassedFromLastMaint = DateTime.Now.Subtract(perfDate); - // Convert in minutes - double minutesPassedFromLastMaint = timePassedFromLastMaint.TotalMinutes; - - // Interval - passed minutes since last maintenance - if (SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value) <= minutesPassedFromLastMaint) - { - // Update last expiration date - if (currMaintenance.LastExpirationDate == null) - { - // Exp date = now - ( expiration intervall) - DateTime expirationDate = DateTime.Now.Subtract(TimeSpan.FromMinutes(minutesPassedFromLastMaint - currMaintenance.Interval.Value)); - - maintenancesController.UpdateLastExpirationDate(currMaintenance.MaintenanceId, expirationDate); - currMaintenance.LastExpirationDate = expirationDate; - } - - // Add item to return list - expiredMaintenance.Add(new DTOExpiredMaintenanceModel() - { - Id = currMaintenance.MaintenanceId, - ExpirationDate = (DateTime)currMaintenance.LastExpirationDate, - CreatedByCms = currMaintenance.UserId == null, - Title = currMaintenance.UserId == null ? "" : currMaintenance.Title, - }); - } - } - break; - } - } - } - - return cmsError; - } - - public CmsError GetUserSoftKeys(out List softKeys) - { - softKeys = new List(); - List plcSoftKeys = new List(); - - // Get softkeys data from PLC - CmsError cmsError = numericalControl.PLC_RUserSoftKeys(ref plcSoftKeys); - if (cmsError.IsError() || plcSoftKeys.Count == 0) - return cmsError; - - foreach (var softKey in SoftKeysConfig) - { - // Check type of the selected softkey - if (softKey.Type == SOFTKEY_TYPE.GROUP) - { - // Foreach subkey create a softkey - foreach (var subkey in softKey.SubKeys) - { - // Find the plc matching data - var plcSoftKey = plcSoftKeys.Find(x => x.Id == subkey.PlcId); - // Create and add new user softkey model - softKeys.Add(new DTOUserSoftKeyModel() - { - Id = subkey.Id, - Active = plcSoftKey.Active, - Category = softKey.Category, - Value = plcSoftKey.Value - }); - } - } - else - { - // Find the plc matching data - var plcSoftKey = plcSoftKeys.Find(x => x.Id == softKey.PlcId); - softKeys.Add(new DTOUserSoftKeyModel() - { - Id = softKey.Id, - Active = plcSoftKey.Active, - Category = softKey.Category, - Value = plcSoftKey.Value - }); - } - } - - return cmsError; - } - - public CmsError GetNcSoftKeys(out List ncSoftKeys) - { - ncSoftKeys = new List(); - List plcSoftKeys = new List(); - - // Get Nc softkeys data from PLC - CmsError cmsError = numericalControl.PLC_RNcSoftKeys(ref plcSoftKeys); - if (cmsError.IsError() || plcSoftKeys.Count == 0) - return cmsError; - - foreach (var softKey in NcSoftKeysConfig) - { - // Find the plc matching data - var plcSoftKey = plcSoftKeys.Find(x => x.Id == softKey.Id); - - // Create and add Nc softkey model - ncSoftKeys.Add(new DTONcSoftKeyModel() - { - Id = softKey.Id, - Active = plcSoftKey.Active, - Value = plcSoftKey.Value - }); - } - - return cmsError; - } - - public CmsError GetHeadsData(out List heads) - { - // Returned value - heads = new List(); - // Number of configured heads - int headsNumber = HeadsConfig.Count; - - List plcHeads = new List(); - // Read value from PLC - CmsError cmsError = numericalControl.PLC_RHeadsData(plcHeads, headsNumber); - if (cmsError.IsError()) - return cmsError; - - foreach (var head in plcHeads) - { - // Get current head config - var configuredHead = HeadsConfig.Find(x => x.Id == head.Id); - // Create different model according on type - switch (configuredHead.Type) - { - case HEAD_TYPE.SPINDLE: - { - heads.Add(new DTOSpindleModel() - { - Id = head.Id, - Process = head.Process, - Type = configuredHead.Type.ToString(), - inWarning = head.Load_Abrasive >= configuredHead.WarningLimit, - inAlarm = head.Load_Abrasive >= configuredHead.AlarmLimit, - OverrideEditable = head.OverrideEditable, - ActualSpeed = head.ActualSpeed_Pressure, - Load = head.Load_Abrasive, - MountedTool = (ushort)head.MountedTool_Vacum, - Override = head.Override, - IsActive = head.IsActive, - IsSelected = head.IsSelected, - AbrasiveIsActive = head.AbrasiveIsActive - }); - } - break; - - case HEAD_TYPE.AWJ: - { - float vacumValue = (float)head.MountedTool_Vacum / 100; - heads.Add(new DTOAbrasiveWaterJet() - { - Id = head.Id, - Process = head.Process, - Type = configuredHead.Type.ToString(), - inWarning = vacumValue >= configuredHead.WarningLimit, - inAlarm = vacumValue >= configuredHead.AlarmLimit, - OverrideEditable = head.OverrideEditable, - ActualPressure = head.ActualSpeed_Pressure, - Abrasive = head.Load_Abrasive, - Vacum = vacumValue, - Override = head.Override, - IsActive = head.IsActive, - IsSelected = head.IsSelected, - AbrasiveIsActive = head.AbrasiveIsActive - }); - } - break; - - case HEAD_TYPE.WJ: - { - heads.Add(new DTOWaterJet() - { - Id = head.Id, - Process = head.Process, - Type = configuredHead.Type.ToString(), - inAlarm = false, - inWarning = false, - ActualPressure = head.ActualSpeed_Pressure, - OverrideEditable = head.OverrideEditable, - Override = head.Override, - IsActive = head.IsActive, - IsSelected = head.IsSelected, - AbrasiveIsActive = head.AbrasiveIsActive - }); - } - break; - } - } - - return cmsError; - } - - public CmsError ReadAxisData(out List axesNames) - { - axesNames = new List(); - List plcAxes = new List(); - // Read selected process - ushort selectedProcess = 0; - CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); - if (cmsError.IsError()) - return cmsError; - - if (selectedProcess != 0) - { - // Read axes names - cmsError = numericalControl.AXES_RAxesNames(selectedProcess, ref plcAxes); - - axesNames = plcAxes.Select(x => new DTOAxisNameModel() - { - Id = x.Id, - Name = x.Name - }).ToList(); - } - - return cmsError; - } - - public CmsError GetToolTableConfiguration(out ToolTableConfiguration config) - { - config = new ToolTableConfiguration(); - CmsError cmsError = numericalControl.TOOLS_RConfiguration(ref config); - - if (NcConfig.NcVendor != NC_VENDOR.SIEMENS && NcConfig.NcVendor != NC_VENDOR.DEMO) - { - config.FamilyOptionActive = true; - config.MultitoolOptionActive = true; - - List categories = new List(); - if (!ToolManagerConfig.ShankOpt) - { - config.MultitoolOptionActive = false; - categories.Add("shankType"); - } - - if (!ToolManagerConfig.FamilyOpt) - { - config.FamilyOptionActive = false; - categories.Add("family"); - config.ToolsConfiguration = config.ToolsConfiguration.Where(x => !(x.Name == "familyId")).ToList(); - config.ToolsConfiguration = config.ToolsConfiguration.Concat(config.FamiliesConfiguration.FamilyConfiguration).ToList(); - } - else - { - config.ToolsConfiguration = config.ToolsConfiguration.Concat(config.FamiliesConfiguration.FamilyReadOnlyConfiguration).ToList(); - } - - if (!ToolManagerConfig.OffsetOpt) - categories.Add("offset"); - - if (!ToolManagerConfig.TcpOpt) - categories.Add("tcp"); - - if (!ToolManagerConfig.CoolingOpt) - categories.Add("cooling"); - - if (!ToolManagerConfig.DynamicCompensationOpt) - categories.Add("dynamicCompensation"); - - if (!ToolManagerConfig.GammaOpt) - categories.Add("gamma"); - - if (!ToolManagerConfig.LifeOpt) - categories.Add("life"); - - if (!ToolManagerConfig.MagPosTypeOpt) - categories.Add("magPos"); - - if (!ToolManagerConfig.MultidimensionalShankOpt) - categories.Add("shankOpt"); - - if (!ToolManagerConfig.SelfAdaptivePathOpt) - categories.Add("selfAdaptive"); - - if (!ToolManagerConfig.BallufOpt) - categories.Add("balluf"); - - config.ToolsConfiguration = config.ToolsConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); - config.FamiliesConfiguration.FamilyConfiguration = config.FamiliesConfiguration.FamilyConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); - config.ShanksConfiguration.ShankConfiguration = config.ShanksConfiguration.ShankConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); - config.MagazinePosConfiguration = config.MagazinePosConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); - - } - - return cmsError; - } - - public CmsError GetToolTableData(out List config) - { - config = new List(); - return numericalControl.TOOLS_RToolsData(ref config); - } - - public CmsError GetShanksData(out List shanks) - { - shanks = new List(); - return numericalControl.TOOLS_RShanksData(ref shanks); - } - - public CmsError GetFamiliesData(out List families) - { - families = new List(); - return numericalControl.TOOLS_RFamilyData(ref families); - } - - public CmsError GetMagazinesPositionsData(out List magazinesPositions) - { - magazinesPositions = new List(); - return numericalControl.TOOLS_RMagazinePositions(ref magazinesPositions); - } - - public CmsError GetMagazineStatus(out DTOMagazineActionModel magazineStatus) - { - // Set up models - magazineStatus = new DTOMagazineActionModel(); - MagazineActionModel libModel = new MagazineActionModel(); - // Read status from NC - CmsError cmsError = numericalControl.TOOLS_GetMagazinesStatus(ref libModel); - if (cmsError.IsError()) - return cmsError; - - magazineStatus = (DTOMagazineActionModel)libModel; - - return cmsError; - } - - #endregion Read Data - - #region Write data - - public CmsError PutNcSoftKeyClick(uint id) - { - // Write Nc softkey press to plc - CmsError cmsError = numericalControl.PLC_WNcSoftKey(id); - - return cmsError; - } - - public CmsError PutUserSoftKeyClick(uint id) - { - // Write user softkey press to plc - return numericalControl.PLC_WUserSoftKey(id); - } - - public CmsError RefreshAlarm(uint id) - { - // Write in memory the request to refresh the alarm - return numericalControl.PLC_WRefreshMessage(id); - } - - public CmsError RestoreAlarm(uint id) - { - // Write in memory the request to restore the alarm - return numericalControl.PLC_WRestoreMessage(id); - } - - public CmsError RefreshAllAlarms() - { - return numericalControl.PLC_WRefreshAllMessages(); - } - - public CmsError PutSelectProcess(ushort procNumber) - { - return numericalControl.PROC_WSelectProcess(procNumber); - } - - public CmsError PutSelectAxis(byte axisId) - { - return numericalControl.AXES_WSelectAxis(axisId); - } - - public CmsError PutOverride(uint id, string action) - { - HEAD_OVERRIDE_SIGN sign = HEAD_OVERRIDE_SIGN.MINUS; - if (action == "plus") - sign = HEAD_OVERRIDE_SIGN.PLUS; - - CmsError cmsError = numericalControl.PLC_WHeadOverride(id, sign); - return cmsError; - } - - public CmsError PutPowerOnData(uint id) - { - // Set to true power on data by id - return numericalControl.PLC_WPowerOnData(id, true); +using CMS_CORE; +using CMS_CORE.Demo; +using CMS_CORE.Fanuc; +using CMS_CORE.Osai; +using CMS_CORE.Siemens; +using Step.Database.Controllers; +using Step.Model.DatabaseModels; +using Step.Model.DTOModels; +using Step.Model.DTOModels.MaintenanceModels; +using Step.Model.DTOModels.ToolModels; +using Step.Utils; +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Globalization; +using System.IO; +using System.Linq; +using static CMS_CORE_Library.DataStructures; +using static Step.Config.ServerConfig; +using static Step.Model.Constants; + +namespace Step.NC +{ + public class NcHandler : IDisposable + { + public Nc numericalControl; + + public NcHandler() + { + // Choose NC + numericalControl = SetNumericalControl(); + } + + public void Dispose() + { + if(NcConfig.NcVendor != NC_VENDOR.SIEMENS) + numericalControl.NC_Disconnect(); + } + + public CmsError Connect() + { + // Connect NC + if (!numericalControl.NC_IsConnected()) + return numericalControl.NC_Connect(); + + return null; + } + + public Nc SetNumericalControl() + { + // Return new Numerical control instance choosed from the configuration + switch (NcConfig.NcVendor) + { + case NC_VENDOR.DEMO: + return new Nc_Demo(NcConfig.NcIpAddress, NcConfig.NcPort); + + case NC_VENDOR.FANUC: + return new Nc_Fanuc(NcConfig.NcIpAddress, NcConfig.NcPort, 2000); + + case NC_VENDOR.SIEMENS: + return new Nc_Siemens(2000); + + case NC_VENDOR.OSAI: + return new Nc_Osai(NcConfig.NcIpAddress, NcConfig.NcPort, 2000); + } + return null; + } + + #region File manager + + public CmsError GetFileList(string path, out List fileList) + { + fileList = new List(); + return numericalControl.FILES_RGetFileList(path, ref fileList); + } + + public CmsError GetFileInfo(string path, out InfoFile fileInfo) + { + fileInfo = new InfoFile(); + return numericalControl.FILES_RGetFileInfo(path, ref fileInfo); + } + + public CmsError GetActiveProgramInfo(out DTOActiveProgramDataModel dtoData) + { + dtoData = new DTOActiveProgramDataModel(); + // Get selectedProcess process id + ushort selectedProcess = 0; + CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); + if (cmsError.IsError()) + return cmsError; + + ActiveProgramDataModel data = new ActiveProgramDataModel(); + cmsError = numericalControl.FILES_RActiveProgramData(selectedProcess, ref data); + + dtoData = new DTOActiveProgramDataModel() + { + IsoLines = data.IsoLines, + Path = data.Path, + TimeLeft = data.TimeLeft + }; + + return cmsError; + } + + public CmsError SetActiveProgramInfo(string path, out DTOActiveProgramDataModel dtoData) + { + dtoData = new DTOActiveProgramDataModel(); + // Get selectedProcess process id + ushort selectedProcess = 0; + CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); + if (cmsError.IsError()) + return cmsError; + + ActiveProgramDataModel data = new ActiveProgramDataModel(); + cmsError = numericalControl.FILES_WSetActiveProgram(selectedProcess, path, ref data); + + dtoData = new DTOActiveProgramDataModel() + { + IsoLines = data.IsoLines, + Path = data.Path, + TimeLeft = data.TimeLeft + }; + + return cmsError; + } + + public CmsError DeactivateProgram(out DTOActiveProgramDataModel dtoData) + { + dtoData = new DTOActiveProgramDataModel(); + // Get selectedProcess process id + ushort selectedProcess = 0; + CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); + if (cmsError.IsError()) + return cmsError; + + ActiveProgramDataModel data = new ActiveProgramDataModel(); + cmsError = numericalControl.FILES_WDeactivateProgram(selectedProcess); + + dtoData = new DTOActiveProgramDataModel() + { + IsoLines = data.IsoLines, + Path = data.Path, + TimeLeft = data.TimeLeft + }; + + return cmsError; + } + + public CmsError UploadPartProgramAndActivate(string localPath, string fileName, out ActiveProgramDataModel programData) + { + programData = new ActiveProgramDataModel(); + // Upload to NC + string newFilePath = ""; + CmsError cmsError = numericalControl.FILES_UploadPartProgram(localPath, fileName, ref newFilePath); + if (cmsError.IsError()) + return cmsError; + + // Delete local file + File.Delete(localPath + "\\" + fileName); + + // Get selectedProcess id + ushort selectedProcess = 0; + cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); + if (cmsError.IsError()) + return cmsError; + // Activate + return numericalControl.FILES_WSetActiveProgram(selectedProcess, newFilePath, ref programData); + } + + #endregion File manager + + #region Read Data + + public CmsError GetNcGenericData(out DTONcGenericDataModel genericData) + { + genericData = new DTONcGenericDataModel(MachineConfig.Model); + + // Get date time + DateTime dateTime = new DateTime(); + CmsError cmsError = numericalControl.NC_RDateTime(ref dateTime); + if (cmsError.errorCode != 0) + return cmsError; + + genericData.DateTime = dateTime; + + // Get language + CultureInfo lang = new CultureInfo("en"); + cmsError = numericalControl.NC_RLanguage(ref lang); + if (cmsError.errorCode != 0) + return cmsError; + + genericData.Language = lang.TwoLetterISOLanguageName; + + string tmpInfo = ""; + // Get serial number + cmsError = numericalControl.NC_RSerialNumber(ref tmpInfo); + if (cmsError.errorCode != 0) + return cmsError; + + genericData.SerialNumber = tmpInfo; + + // Get software version + numericalControl.NC_RSoftwareVersion(ref tmpInfo); + if (cmsError.errorCode != 0) + return cmsError; + + genericData.NcSoftwareVersion = tmpInfo; + + // Get model name + numericalControl.NC_RModelName(ref tmpInfo); + if (cmsError.errorCode != 0) + return cmsError; + + genericData.NcModel = tmpInfo; + + // Get machine number + numericalControl.NC_RMachineNumber(ref tmpInfo); + if (cmsError.errorCode != 0) + return cmsError; + + genericData.CmsMachineIdNumber = tmpInfo; + + // Get max process number + ushort procNum = 0; + numericalControl.NC_RProcessesNum(ref procNum); + if (cmsError.errorCode != 0) + return cmsError; + // Max process number + genericData.ProcessNumber = procNum; + // Get Installation Date + genericData.InstallationDate = DateTime.Now; + // Get PLC version + genericData.PlcVersion = "1.0.0"; + // Get PLC version + genericData.UnitOfMeasurement = "mm"; + + return cmsError; + } + + public CmsError GetNcAlarms(out DTOAlarmsModel alarms) + { + alarms = new DTOAlarmsModel(); + List tmpAlarms = new List(); + + // Read NC active alarms + CmsError cmsError = numericalControl.NC_RActiveAlarms(ref tmpAlarms); + if (cmsError.errorCode != 0) + return cmsError; + + // Create response list from NC data + foreach (AlarmModel ncAlarm in tmpAlarms) + { + alarms.NcAlarms.Add(new GenericAlarmModel() + { + Id = ncAlarm.Id, + Message = ncAlarm.Message, + DateTime = DateTime.Now, + RestorationIsEnabled = false + }); + } + // Select distints + //alarms.NcAlarms = alarms.NcAlarms?.GroupBy(x => x.Id).Select(x => x.First()).ToList(); + + // Get NC max process number + ushort maxProcNumber = 0; + cmsError = numericalControl.NC_RProcessesNum(ref maxProcNumber); + if (cmsError.errorCode != 0) + return cmsError; + + // For each process + for (ushort i = 1; i <= maxProcNumber; i++) + { + // Get process active alarms + numericalControl.PROC_RActiveAlarms(i, ref tmpAlarms); + // Create response list from NC data + foreach (AlarmModel processAlarm in tmpAlarms) + { + alarms.ProcessAlarms.Add(new ProcessAlarmModel() + { + Id = processAlarm.Id, + Message = processAlarm.Message, + Process = processAlarm.Process, + DateTime = DateTime.Now, + RestorationIsEnabled = false + }); + } + } + + List plcAlarms = new List(); + // Read PLC Active Messages + cmsError = numericalControl.PLC_RActiveMessages(ref plcAlarms); + bool restorationIsEnabled = false; + foreach (PlcAlarmModel plcAlarm in plcAlarms) + { + var configuredAlarm = InitialAlarmsConfig.Where(x => x.PlcId == plcAlarm.Id).FirstOrDefault(); + if (configuredAlarm == null) + restorationIsEnabled = false; + else + restorationIsEnabled = configuredAlarm.RestoreIsActive; + + // Create response list from plc data + alarms.PlcAlarms.Add(new DTOPlcAlarmModel() + { + Id = plcAlarm.Id, + IsWarning = plcAlarm.IsWarning, + Process = plcAlarm.Process, + RestorationIsActive = plcAlarm.RestorationIsActive, + DateTime = DateTime.Now, + RestorationIsEnabled = restorationIsEnabled + }); + } + if (cmsError.errorCode != 0) + return cmsError; + + return cmsError; + } + + public CmsError GetAxesPositions(out List axes) + { + axes = new List(); + + // Get NC max process number + ushort maxProcNumber = 0; + CmsError cmsError = numericalControl.NC_RProcessesNum(ref maxProcNumber); + if (cmsError.errorCode != 0) + return cmsError; + + // For each process + for (ushort i = 1; i <= maxProcNumber; i++) + { + cmsError = GetAxesPositionsByProcess(i, out DTOAxesModel axis); + if (cmsError.errorCode != 0) + return cmsError; + + axes.Add(axis); + } + + return cmsError; + } + + public CmsError GetAxesPositionsBySelectedProcess(out DTOAxesModel axes) + { + axes = new DTOAxesModel(); + + // Get selectedProcess process number + ushort selectedProcess = 0; + CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); + if (cmsError.IsError()) + return cmsError; + + if (selectedProcess > 0) + cmsError = GetAxesPositionsByProcess(selectedProcess, out axes); + + return cmsError; + } + + public CmsError GetAxesPositionsByProcess(ushort processNum, out DTOAxesModel axes) + { + axes = new DTOAxesModel(); + + CmsError cmsError = numericalControl.AXES_RInterpPosition(processNum, ref axes.interpolated); + if (cmsError.errorCode != 0) + return cmsError; + + cmsError = numericalControl.AXES_RMachinePosition(processNum, ref axes.machine); + if (cmsError.errorCode != 0) + return cmsError; + + cmsError = numericalControl.AXES_RProgrPosition(processNum, ref axes.programmePos); + if (cmsError.errorCode != 0) + return cmsError; + + cmsError = numericalControl.AXES_RDistanceToGo(processNum, ref axes.toGo); + if (cmsError.errorCode != 0) + return cmsError; + + //numericalControl.AXES_RFollowingError(1, ref axes.followingErr); + + return cmsError; + } + + public CmsError GetPowerOnData(out DTOPowerOnDataModel resultPowerOnData) + { + resultPowerOnData = new DTOPowerOnDataModel(); + + // Init object + PreAndPostPowerOnModel prePostPowerOn = new PreAndPostPowerOnModel + { + PostPowerOn = new PostPowerOnModel(), + PrePowerOn = new PrePowerOnModel() + }; + + // Read pre and post power on data + CmsError cmsError = numericalControl.PLC_RPowerOnData(ref prePostPowerOn); + if (cmsError.IsError()) + return cmsError; + + // Set return object + resultPowerOnData.PrePowerOn = prePostPowerOn.PrePowerOn; + resultPowerOnData.PostPowerOn = prePostPowerOn.PostPowerOn; + + // Read axes procedure data from + cmsError = numericalControl.PLC_RAxesResetData(ref resultPowerOnData.AxisReset); + + return cmsError; + } + + public CmsError GetProcessesData(out DTOProcessesDataModel processesData) + { + processesData = new DTOProcessesDataModel(); + + // Get NC max process number + ushort maxProcNumber = 0; + CmsError cmsError = numericalControl.NC_RProcessesNum(ref maxProcNumber); + if (cmsError.errorCode != 0) + return cmsError; + + PROC_STATUS status = PROC_STATUS.IDLE; + // For each process get info + for (ushort i = 1; i <= maxProcNumber; i++) + { + ProcessDataModel tmpProcPP = new ProcessDataModel(); + // Get process status + cmsError = numericalControl.PROC_RStatusAndData(i, ref tmpProcPP); + // if at least one process is in run, NC is running + // Add to list + processesData.processes.Add(new ProcessModel() + { + Id = tmpProcPP.Id, + IsInAlarm = tmpProcPP.IsInAlarm, + PartProgramName = tmpProcPP.PartProgramName, + Reps = tmpProcPP.Reps, + Status = tmpProcPP.Status, + Type = tmpProcPP.Type, + Visible = tmpProcPP.Visible + }); + + if (tmpProcPP.IsSelected && processesData.selectedProcess == 0) // TODO remove with multi-process + processesData.selectedProcess = (ushort)tmpProcPP.Id; + + // Check if there are running process + if (!processesData.isRunning) + { + // Get process status + cmsError = numericalControl.PROC_RStatus(i, ref status); + if (status == PROC_STATUS.RUN || status == PROC_STATUS.HOLD) + processesData.isRunning = true; + else + status = PROC_STATUS.IDLE; + } + } + // Read selected axes + cmsError = numericalControl.AXES_RSelectedAxis(ref processesData.selectedAxis); + + return cmsError; + } + + public CmsError GetFunctionsMappedWithNC(out List functionsAccessList) + { + functionsAccessList = new List(); + // Read plc functionality + List functionalityList = null; + CmsError cmsError = numericalControl.PLC_RFunctionAccess(ref functionalityList); + + // If empty return + if (functionalityList == null || functionalityList.Count == 0) + return cmsError; + + using (FunctionsAccessController functionsAccessController = new FunctionsAccessController()) + { + // Map plc and database data + functionsAccessList = functionsAccessController.GetFunctionsMappedWithPlc(functionalityList); + } + + return cmsError; + } + + public CmsError GetMainenancesCounter(out List counters) + { + // List of PLC counters + counters = new List(); + // Get counters values from PLC + CmsError cmsError = numericalControl.PLC_RMachineCounters(ref counters); + + return cmsError; + } + + public CmsError GetMaintenanceData(out DTOMaintenanceModel dTOMaintenance, int maintenanceId, int userId) + { + dTOMaintenance = null; + + CmsError cmsError = GetMaintenances(out List maintenance, userId); + if (cmsError.IsError()) + return cmsError; + + dTOMaintenance = maintenance.Where(x => x.Id == maintenanceId).FirstOrDefault(); + + return cmsError; + } + + public CmsError GetMaintenances(out List dtoMaintenancesModel, int userId) + { + // Return value + dtoMaintenancesModel = new List(); + + // Get counters values from PLC + CmsError cmsError = GetMainenancesCounter(out List counters); + if (cmsError.IsError()) + return cmsError; + + using (MaintenancesController maintenancesController = new MaintenancesController()) + { + // Get the last performed maintenance for each maintenance + List performedMaintenances = maintenancesController.FindLastPerformedMaintenances(); + + // Get all the active maintenances + List maintenances = maintenancesController.FindAll(); // TODO db or config file? + + double percentage = 0; + TimeSpan missingDays = new TimeSpan(); + CounterModel counter = new CounterModel(); + + bool canEdit = false; + bool canPerform = false; + foreach (var currMaintenance in maintenances) + { + // Get matching last performed maintenance for current maintenance + PerformedMaintenanceModel performed = performedMaintenances.Find(x => x.MaintenanceId == currMaintenance.MaintenanceId); + + switch (currMaintenance.Type) + { + case MAINTENANCE_TYPE.MACHINE_INTERVAL: + { + // Get matching counter for the current maintenance + counter = counters.Find(x => x.Id == currMaintenance.CounterId); + + int perfVal = 0; + if (performed != null) + perfVal = performed.CounterValue; + // Calc percentage = PLC - PERFORMED VALUE * 100 / interval + percentage = ((counter.Value - perfVal) * 100) / SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value); + missingDays = TimeSpan.FromMinutes(-1 * (counter.Value - perfVal - SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value))); + } + break; + + case MAINTENANCE_TYPE.EXP_DATE: + { + // Already performed + if (performed != null) + { + percentage = 100; + missingDays = TimeSpan.Zero; + } + else + { + percentage = ((DateTime.Now - currMaintenance.CreationDate).TotalMinutes * 100) / (currMaintenance.Deadline - currMaintenance.CreationDate).TotalMinutes; // TimeSpan.FromTicks((currMaintenance.Deadline.Ticks)).TotalMinutes + missingDays = currMaintenance.Deadline - DateTime.Now; + counter = new CounterModel(); + } + } + break; + + case MAINTENANCE_TYPE.TIME_INTERVAL: + { + // Get last performed, default value is maintenance creation date + DateTime perfDate = currMaintenance.CreationDate; + if (performed != null) + perfDate = performed.Date; + + // Now - last performed = elapsed interval since last maintenance + TimeSpan timePassedFromLastMaint = DateTime.Now.Subtract(perfDate); + // Convert in minutes + double minutesPassedFromLastMaint = timePassedFromLastMaint.TotalMinutes; + + // percentage = Passed minutes since last maintenance * 100 / Interval + percentage = (minutesPassedFromLastMaint * 100) / SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value); + missingDays = TimeSpan.FromMinutes(-1 * (minutesPassedFromLastMaint - SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value))); + counter = new CounterModel(); + } + break; + } + + canEdit = false; + using (MachinesUsersController machineUsersController = new MachinesUsersController()) + { + if (currMaintenance.UserId != null) + { + // Check if user can edit the maintenance -> caller id - maintenance user id + int comparision = machineUsersController.CompareUsersRole(userId, currMaintenance.UserId.Value, MachineConfig.MachineId); + if (comparision >= 0) + canPerform = canEdit = true; + } + else + { + canEdit = false; + canPerform = machineUsersController.UserIsCmsAdmin(MachineConfig.MachineId, userId); + } + } + + dtoMaintenancesModel.Add(new DTOMaintenanceModel() + { + Id = currMaintenance.MaintenanceId, + Title = currMaintenance.UserId == null ? "" : currMaintenance.Title, + CreationDate = currMaintenance.CreationDate, + Deadline = currMaintenance.Deadline, + Description = currMaintenance.Description, + Interval = currMaintenance.Interval, + LastExpirationDate = currMaintenance.LastExpirationDate, + LastPerformedDate = performed?.Date, + Type = currMaintenance.Type.ToString(), + UnitOfMeasure = currMaintenance.UnitOfMeasure.ToString(), + PercentageOfCompletion = percentage >= 0 && percentage < 100 ? Convert.ToInt32(percentage) : 100, + MissingDays = missingDays, + PlcCounter = counter.Value, + CreatedByCms = currMaintenance.UserId == null, + CanEdit = canEdit, + CanPerform = canPerform + }); + } + } + + return cmsError; + } + + public CmsError GetExpiredMaintenances(out List expiredMaintenance) + { + // Return value + expiredMaintenance = new List(); + + // Get counters values from PLC + CmsError cmsError = GetMainenancesCounter(out List counters); + if (cmsError.IsError()) + return cmsError; + + using (MaintenancesController maintenancesController = new MaintenancesController()) + { + // Get the last performed maintenance for each maintenance + List performedMaintenances = maintenancesController.FindLastPerformedMaintenances(); + + // Get all the active maintenances + List maintenances = maintenancesController.FindAll(); // TODO db or config file? + + foreach (var currMaintenance in maintenances) + { + // Get matching last performed maintenance for current maintenance + var performed = performedMaintenances.Find(x => x.MaintenanceId == currMaintenance.MaintenanceId); + + switch (currMaintenance.Type) + { + case MAINTENANCE_TYPE.MACHINE_INTERVAL: + { + // Get matching counter for the current maintenance + var counter = counters.Find(x => x.Id == currMaintenance.CounterId); + + int perfVal = 0; + if (performed != null) + perfVal = performed.CounterValue; + + // MAINTENANCE_INTERVAL <= PLC - LASTPERFORMED + if (SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value) <= counter.Value - perfVal) + { + // Update last expiration date if null + if (currMaintenance.LastExpirationDate == null) + { + maintenancesController.UpdateLastExpirationDate(currMaintenance.MaintenanceId, DateTime.Now); + currMaintenance.LastExpirationDate = DateTime.Now; + } + + // Add item to return list + expiredMaintenance.Add(new DTOExpiredMaintenanceModel() + { + Id = currMaintenance.MaintenanceId, + ExpirationDate = (DateTime)currMaintenance.LastExpirationDate, + CreatedByCms = currMaintenance.UserId == null, + Title = currMaintenance.UserId == null ? "" : currMaintenance.Title + }); + } + } + break; + + case MAINTENANCE_TYPE.EXP_DATE: + { + // Already performed + if (performed != null) + break; + // DEADLINE <= NOW + if (currMaintenance.Deadline <= DateTime.Now) + { + maintenancesController.UpdateLastExpirationDate(currMaintenance.MaintenanceId, currMaintenance.Deadline); + currMaintenance.LastExpirationDate = currMaintenance.Deadline; + + // Add item to return list + expiredMaintenance.Add(new DTOExpiredMaintenanceModel() + { + Id = currMaintenance.MaintenanceId, + ExpirationDate = currMaintenance.Deadline, + CreatedByCms = currMaintenance.UserId == null, + Title = currMaintenance.UserId == null ? "" : currMaintenance.Title, + }); + } + } + break; + + case MAINTENANCE_TYPE.TIME_INTERVAL: + { + // Get last performed, default value is maintenance creation date + DateTime perfDate = currMaintenance.CreationDate; + if (performed != null) + perfDate = performed.Date; + + // Now - last performed = elapsed interval since last maintenance + TimeSpan timePassedFromLastMaint = DateTime.Now.Subtract(perfDate); + // Convert in minutes + double minutesPassedFromLastMaint = timePassedFromLastMaint.TotalMinutes; + + // Interval - passed minutes since last maintenance + if (SupportFunctions.ConvertInMinutes(currMaintenance.Interval.Value, currMaintenance.UnitOfMeasure.Value) <= minutesPassedFromLastMaint) + { + // Update last expiration date + if (currMaintenance.LastExpirationDate == null) + { + // Exp date = now - ( expiration intervall) + DateTime expirationDate = DateTime.Now.Subtract(TimeSpan.FromMinutes(minutesPassedFromLastMaint - currMaintenance.Interval.Value)); + + maintenancesController.UpdateLastExpirationDate(currMaintenance.MaintenanceId, expirationDate); + currMaintenance.LastExpirationDate = expirationDate; + } + + // Add item to return list + expiredMaintenance.Add(new DTOExpiredMaintenanceModel() + { + Id = currMaintenance.MaintenanceId, + ExpirationDate = (DateTime)currMaintenance.LastExpirationDate, + CreatedByCms = currMaintenance.UserId == null, + Title = currMaintenance.UserId == null ? "" : currMaintenance.Title, + }); + } + } + break; + } + } + } + + return cmsError; + } + + public CmsError GetUserSoftKeys(out List softKeys) + { + softKeys = new List(); + List plcSoftKeys = new List(); + + // Get softkeys data from PLC + CmsError cmsError = numericalControl.PLC_RUserSoftKeys(ref plcSoftKeys); + if (cmsError.IsError() || plcSoftKeys.Count == 0) + return cmsError; + + foreach (var softKey in SoftKeysConfig) + { + // Check type of the selected softkey + if (softKey.Type == SOFTKEY_TYPE.GROUP) + { + // Foreach subkey create a softkey + foreach (var subkey in softKey.SubKeys) + { + // Find the plc matching data + var plcSoftKey = plcSoftKeys.Find(x => x.Id == subkey.PlcId); + // Create and add new user softkey model + softKeys.Add(new DTOUserSoftKeyModel() + { + Id = subkey.Id, + Active = plcSoftKey.Active, + Category = softKey.Category, + Value = plcSoftKey.Value + }); + } + } + else + { + // Find the plc matching data + var plcSoftKey = plcSoftKeys.Find(x => x.Id == softKey.PlcId); + softKeys.Add(new DTOUserSoftKeyModel() + { + Id = softKey.Id, + Active = plcSoftKey.Active, + Category = softKey.Category, + Value = plcSoftKey.Value + }); + } + } + + return cmsError; + } + + public CmsError GetNcSoftKeys(out List ncSoftKeys) + { + ncSoftKeys = new List(); + List plcSoftKeys = new List(); + + // Get Nc softkeys data from PLC + CmsError cmsError = numericalControl.PLC_RNcSoftKeys(ref plcSoftKeys); + if (cmsError.IsError() || plcSoftKeys.Count == 0) + return cmsError; + + foreach (var softKey in NcSoftKeysConfig) + { + // Find the plc matching data + var plcSoftKey = plcSoftKeys.Find(x => x.Id == softKey.Id); + + // Create and add Nc softkey model + ncSoftKeys.Add(new DTONcSoftKeyModel() + { + Id = softKey.Id, + Active = plcSoftKey.Active, + Value = plcSoftKey.Value + }); + } + + return cmsError; + } + + public CmsError GetHeadsData(out List heads) + { + // Returned value + heads = new List(); + // Number of configured heads + int headsNumber = HeadsConfig.Count; + + List plcHeads = new List(); + // Read value from PLC + CmsError cmsError = numericalControl.PLC_RHeadsData(plcHeads, headsNumber); + if (cmsError.IsError()) + return cmsError; + + foreach (var head in plcHeads) + { + // Get current head config + var configuredHead = HeadsConfig.Find(x => x.Id == head.Id); + // Create different model according on type + switch (configuredHead.Type) + { + case HEAD_TYPE.SPINDLE: + { + heads.Add(new DTOSpindleModel() + { + Id = head.Id, + Process = head.Process, + Type = configuredHead.Type.ToString(), + inWarning = head.Load_Abrasive >= configuredHead.WarningLimit, + inAlarm = head.Load_Abrasive >= configuredHead.AlarmLimit, + OverrideEditable = head.OverrideEditable, + ActualSpeed = head.ActualSpeed_Pressure, + Load = head.Load_Abrasive, + MountedTool = (ushort)head.MountedTool_Vacum, + Override = head.Override, + IsActive = head.IsActive, + IsSelected = head.IsSelected, + AbrasiveIsActive = head.AbrasiveIsActive + }); + } + break; + + case HEAD_TYPE.AWJ: + { + float vacumValue = (float)head.MountedTool_Vacum / 100; + heads.Add(new DTOAbrasiveWaterJet() + { + Id = head.Id, + Process = head.Process, + Type = configuredHead.Type.ToString(), + inWarning = vacumValue >= configuredHead.WarningLimit, + inAlarm = vacumValue >= configuredHead.AlarmLimit, + OverrideEditable = head.OverrideEditable, + ActualPressure = head.ActualSpeed_Pressure, + Abrasive = head.Load_Abrasive, + Vacum = vacumValue, + Override = head.Override, + IsActive = head.IsActive, + IsSelected = head.IsSelected, + AbrasiveIsActive = head.AbrasiveIsActive + }); + } + break; + + case HEAD_TYPE.WJ: + { + heads.Add(new DTOWaterJet() + { + Id = head.Id, + Process = head.Process, + Type = configuredHead.Type.ToString(), + inAlarm = false, + inWarning = false, + ActualPressure = head.ActualSpeed_Pressure, + OverrideEditable = head.OverrideEditable, + Override = head.Override, + IsActive = head.IsActive, + IsSelected = head.IsSelected, + AbrasiveIsActive = head.AbrasiveIsActive + }); + } + break; + } + } + + return cmsError; + } + + public CmsError ReadAxisData(out List axesNames) + { + axesNames = new List(); + List plcAxes = new List(); + // Read selected process + ushort selectedProcess = 0; + CmsError cmsError = numericalControl.PROC_RSelectedProcess(ref selectedProcess); + if (cmsError.IsError()) + return cmsError; + + if (selectedProcess != 0) + { + // Read axes names + cmsError = numericalControl.AXES_RAxesNames(selectedProcess, ref plcAxes); + + axesNames = plcAxes.Select(x => new DTOAxisNameModel() + { + Id = x.Id, + Name = x.Name + }).ToList(); + } + + return cmsError; + } + + public CmsError GetToolTableConfiguration(out ToolTableConfiguration config) + { + config = new ToolTableConfiguration(); + CmsError cmsError = numericalControl.TOOLS_RConfiguration(ref config); + + if (NcConfig.NcVendor != NC_VENDOR.SIEMENS && NcConfig.NcVendor != NC_VENDOR.DEMO) + { + config.FamilyOptionActive = true; + config.MultitoolOptionActive = true; + + List categories = new List(); + if (!ToolManagerConfig.ShankOpt) + { + config.MultitoolOptionActive = false; + categories.Add("shankType"); + } + + if (!ToolManagerConfig.FamilyOpt) + { + config.FamilyOptionActive = false; + categories.Add("family"); + config.ToolsConfiguration = config.ToolsConfiguration.Where(x => !(x.Name == "familyId")).ToList(); + config.ToolsConfiguration = config.ToolsConfiguration.Concat(config.FamiliesConfiguration.FamilyConfiguration).ToList(); + } + else + { + config.ToolsConfiguration = config.ToolsConfiguration.Concat(config.FamiliesConfiguration.FamilyReadOnlyConfiguration).ToList(); + } + + if (!ToolManagerConfig.OffsetOpt) + categories.Add("offset"); + + if (!ToolManagerConfig.TcpOpt) + categories.Add("tcp"); + + if (!ToolManagerConfig.CoolingOpt) + categories.Add("cooling"); + + if (!ToolManagerConfig.DynamicCompensationOpt) + categories.Add("dynamicCompensation"); + + if (!ToolManagerConfig.GammaOpt) + categories.Add("gamma"); + + if (!ToolManagerConfig.LifeOpt) + categories.Add("life"); + + if (!ToolManagerConfig.MagPosTypeOpt) + categories.Add("magPos"); + + if (!ToolManagerConfig.MultidimensionalShankOpt) + categories.Add("shankOpt"); + + if (!ToolManagerConfig.SelfAdaptivePathOpt) + categories.Add("selfAdaptive"); + + if (!ToolManagerConfig.BallufOpt) + categories.Add("balluf"); + + config.ToolsConfiguration = config.ToolsConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); + config.FamiliesConfiguration.FamilyConfiguration = config.FamiliesConfiguration.FamilyConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); + config.ShanksConfiguration.ShankConfiguration = config.ShanksConfiguration.ShankConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); + config.MagazinePosConfiguration = config.MagazinePosConfiguration.Where(x => !categories.Contains(x.Category)).ToList(); + + } + + return cmsError; + } + + public CmsError GetToolTableData(out List config) + { + config = new List(); + return numericalControl.TOOLS_RToolsData(ref config); + } + + public CmsError GetShanksData(out List shanks) + { + shanks = new List(); + return numericalControl.TOOLS_RShanksData(ref shanks); + } + + public CmsError GetFamiliesData(out List families) + { + families = new List(); + return numericalControl.TOOLS_RFamilyData(ref families); + } + + public CmsError GetMagazinesPositionsData(out List magazinesPositions) + { + magazinesPositions = new List(); + return numericalControl.TOOLS_RMagazinePositions(ref magazinesPositions); + } + + public CmsError GetMagazineStatus(out DTOMagazineActionModel magazineStatus) + { + // Set up models + magazineStatus = new DTOMagazineActionModel(); + MagazineActionModel libModel = new MagazineActionModel(); + // Read status from NC + CmsError cmsError = numericalControl.TOOLS_GetMagazinesStatus(ref libModel); + if (cmsError.IsError()) + return cmsError; + + magazineStatus = (DTOMagazineActionModel)libModel; + + return cmsError; + } + + #endregion Read Data + + #region Write data + + public CmsError PutNcSoftKeyClick(uint id) + { + // Write Nc softkey press to plc + CmsError cmsError = numericalControl.PLC_WNcSoftKey(id); + + return cmsError; + } + + public CmsError PutUserSoftKeyClick(uint id) + { + // Write user softkey press to plc + return numericalControl.PLC_WUserSoftKey(id); + } + + public CmsError RefreshAlarm(uint id) + { + // Write in memory the request to refresh the alarm + return numericalControl.PLC_WRefreshMessage(id); + } + + public CmsError RestoreAlarm(uint id) + { + // Write in memory the request to restore the alarm + return numericalControl.PLC_WRestoreMessage(id); + } + + public CmsError RefreshAllAlarms() + { + return numericalControl.PLC_WRefreshAllMessages(); + } + + public CmsError PutSelectProcess(ushort procNumber) + { + return numericalControl.PROC_WSelectProcess(procNumber); + } + + public CmsError PutSelectAxis(byte axisId) + { + return numericalControl.AXES_WSelectAxis(axisId); + } + + public CmsError PutOverride(uint id, string action) + { + HEAD_OVERRIDE_SIGN sign = HEAD_OVERRIDE_SIGN.MINUS; + if (action == "plus") + sign = HEAD_OVERRIDE_SIGN.PLUS; + + CmsError cmsError = numericalControl.PLC_WHeadOverride(id, sign); + return cmsError; + } + + public CmsError PutPowerOnData(uint id) + { + // Set to true power on data by id + return numericalControl.PLC_WPowerOnData(id, true); } - public CmsError SetActiveLanguage(CultureInfo language) - { - // Set to true power on data by id - return numericalControl.NC_WLanguage(language); - } - - - #endregion Write data - - #region Siemens Tools - - public CmsError AddTool(ref SiemensToolModel tool) - { - return numericalControl.TOOLS_WAddTool(ref tool); - } - - public CmsError UpdateTool(ref SiemensToolModel tool) - { - return numericalControl.TOOLS_WUpdateTool(ref tool); - } - - public CmsError AddFamily(string name, out FamilyModel family) - { - family = new FamilyModel() - { - Name = name - }; - - return numericalControl.TOOLS_WAddFamily(ref family); - } - - public CmsError AddShank(ref ShankModel shank) - { - return numericalControl.TOOLS_WAddShank(ref shank); - } - - public CmsError UpdateShank(ref ShankModel shank) - { - return numericalControl.TOOLS_WUpdateShank(ref shank); - } - - public CmsError UpdateFamilyName(string oldName, string newName) - { - return numericalControl.TOOLS_WUpdateFamilyData(oldName, newName); - } - - public CmsError UpdateMagazinePosition(PositionModel magazinePosition) - { - return numericalControl.TOOLS_WUpdatePosition(magazinePosition); - } - - public CmsError DeleteTool(int id) - { - return numericalControl.TOOLS_WDeleteTool(id); - } - - public CmsError DeleteShank(int id) - { - return numericalControl.TOOLS_WDeleteShank(id); - } - - public CmsError DeleteFamily(string name) - { - return numericalControl.TOOLS_WDeleteFamily(name); - } - - public CmsError AddEdge(int toolId, ref EdgeModel edge) - { - return numericalControl.TOOLS_WAddEdge(toolId, ref edge); - } - - public CmsError UpdateEdge(int toolId, ref EdgeModel edge) - { - return numericalControl.TOOLS_WUpdateEdge(toolId, ref edge); - } - - public CmsError DeleteEdge(int toolId, int edgeId) - { - return numericalControl.TOOLS_WDeleteEdge(toolId, edgeId); - } - - public CmsError GetMagazinePositionsAndTools(int magazineId, out List magazinePos) - { - magazinePos = new List(); - return numericalControl.TOOLS_RMagazineTools(magazineId, ref magazinePos); - } - - public CmsError GetNotInMagazinesTools(out List multiTools, out List tools) - { - multiTools = new List(); - tools = new List(); - return numericalControl.TOOLS_RAvailableTools(ref multiTools, ref tools); - } - - public CmsError LoadToolInMagazine(int magazineId, NewToolInMagazineModel newMag) - { - return numericalControl.TOOLS_WLoadToolInMagazine(magazineId, newMag); - } - - public CmsError UnloadToolInMagazine(int magazineId, int positionId) - { - return numericalControl.TOOLS_WUnloadToolFromMagazine(magazineId, positionId); - } - - public CmsError LoadTooolIntoShank(int shankId, int positionId, int toolId) - { - return numericalControl.TOOLS_WLoadToolIntoShank(shankId, positionId, toolId); - } - - public CmsError UnloadTooolFromShank(int shankId, int positionId) - { - return numericalControl.TOOLS_WUnloadToolFromShank(shankId, positionId); - } - - #endregion Siemens Tools - - // OSAI FANUC - - #region Osai/Fanuc Tools - - public CmsError GetToolsData(out List dtoTools) - { - dtoTools = new List(); - - CmsError cmsError = NO_ERROR; - - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - List tools = toolsManager.FindToolsWithDependencies(); - - foreach (DbNcToolModel tool in tools) - { - DTONcToolModel dtoTool = (DTONcToolModel)tool; - - cmsError = GetToolData(tool, ref dtoTool); - - dtoTools.Add(dtoTool); - } - - return cmsError; - } - } - - public CmsError GetToolData(DbNcToolModel tool, ref DTONcToolModel dtoTool) - { - dtoTool = (DTONcToolModel)tool; - - OffsetModel offset = new OffsetModel(); - CmsError cmsError = NO_ERROR; - - if (tool.OffsetId1 != null && tool.OffsetId1 != 0) - { - // Read first offset data - cmsError = numericalControl.TOOLS_ROffset((short)tool.OffsetId1, ref offset); - if (cmsError.IsError()) - return cmsError; - - dtoTool.Offset1 = offset; - } - if (tool.OffsetId2 != null && tool.OffsetId2 != 0) - { - // Read second offset data - cmsError = numericalControl.TOOLS_ROffset((short)tool.OffsetId2, ref offset); - if (cmsError.IsError()) - return cmsError; - - dtoTool.Offset2 = offset; - } - if (tool.OffsetId3 != null && tool.OffsetId3 != 0) - { - // Read third offset data - cmsError = numericalControl.TOOLS_ROffset((short)tool.OffsetId3, ref offset); - if (cmsError.IsError()) - return cmsError; - - dtoTool.Offset3 = offset; - } - - return cmsError; - } - - public DTONcToolModel AddTool(DTONewNcToolModel tool) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - DbNcToolModel ncTool = toolsManager.AddTool(tool); - - DTONcToolModel dtoTool = new DTONcToolModel(); - GetToolData(ncTool, ref dtoTool); - - return dtoTool; - } - } - - public CmsError UpdateTool(int toolId, DTONewNcToolModel newTool, out DTONcToolModel toolWithOffsets) - { - toolWithOffsets = new DTONcToolModel(); - - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - // Update database tool Tool - DbNcToolModel dbTool = toolsManager.UpdateTool(toolId, newTool); - - // Populate updated tool with offset data - CmsError cmsError = GetToolData(dbTool, ref toolWithOffsets); - if (cmsError.IsError()) - { - dbContextTransaction.Rollback(); - return cmsError; - } - - bool startIsActive = false; - - // Check if tool is loaded into a shank - if (dbTool.ShankId != null) - { - // Check if shank is loaded into magazine - DbNcShankModel shank = toolsManager.FindShankWithTools(dbTool.ShankId.Value); - if (shank.MagazineId != null) - { - startIsActive = true; - // Create backup of data stored on the NC and block file accesses - cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - { - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - return cmsError; - } - - // Update tool - cmsError = UpdateNcTools(toolsManager); - } - } - - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - - return NO_ERROR; - } - } - } - - public CmsError UpdateNcTools(NcToolManagerController toolsManager = null) - { - if (toolsManager == null) - toolsManager = new NcToolManagerController(); - - List shanks = toolsManager - .FindShanks() - .Where(x => x.MagazineId != null) - .Select(x => (NcShankModel)x) - .ToList(); - - // Get tools - List tools = toolsManager - .FindTools() - .Select(x => (NcToolModel)x) - .Where(x => shanks.Any(y => y.ShankId == x.ShankId)) // Find only mounted tools - .ToList(); - - // Update tools - return numericalControl.TOOLS_WUpdateTools(tools); - } - - public CmsError UpdateOffset(short offsetId, OffsetModel offsetData) - { - return numericalControl.TOOLS_WOffset(offsetId, offsetData); - } - - public CmsError UpdateToolOffsetId(int toolId, int positionId, int offsetId, out DTONcToolModel toolWithOffsets) - { - toolWithOffsets = new DTONcToolModel(); - - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - // Update database tool Tool - DbNcToolModel dbTool = toolsManager.UpdateToolOffset(toolId, positionId, offsetId); - - // Populate updated tool with offset data - CmsError cmsError = GetToolData(dbTool, ref toolWithOffsets); - if (cmsError.IsError()) - { - dbContextTransaction.Rollback(); - return cmsError; - } - - bool startIsActive = false; - - // Check if tool is loaded into a shank - if (dbTool.ShankId != null) - { - // Check if shank is loaded into magazine - DbNcShankModel shank = toolsManager.FindShankWithTools(dbTool.ShankId.Value); - if (shank.MagazineId != null) - { - startIsActive = true; - // Create backup of data stored on the NC and block file accesses - cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - { - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - return cmsError; - } - - // Update tool - cmsError = UpdateNcTools(toolsManager); - } - } - - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - - return NO_ERROR; - } - } - } - - public CmsError DeleteNcTool(DbNcToolModel tool) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - // Delete db tool - toolsManager.DeleteTool(tool.ToolId); - - bool startIsActive = false; - CmsError cmsError = NO_ERROR; - - if (tool.ShankId != null) - { - // If tool is mounted than update Nc data - DbNcShankModel shank = toolsManager.FindShankWithTools(tool.ShankId.Value); - if (shank.MagazineId != null) - { - startIsActive = true; - // Create backup of data stored on the NC and block file accesses - cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - { - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - return cmsError; - } - - // Update Nc data - cmsError = UpdateNcTools(toolsManager); - } - } - - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - - return cmsError; - } - } - } - - public CmsError UpdateShank(int shankId, DTONewNcShankModel dtoShank, out DTONcShankModel shank) - { - shank = new DTONcShankModel(); - - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - bool startIsActive = false; - CmsError cmsError = NO_ERROR; - - // Update db data - shank = (DTONcShankModel)toolsManager.UpdateShank(shankId, dtoShank); - if (shank.MagazineId != null) - { - startIsActive = true; - // Create backup of data stored on the NC and block file accesses - cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - { - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - return cmsError; - } - - // Update nc data - cmsError = UpdateNcShank(toolsManager); - } - - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - - return cmsError; - } - } - } - - public CmsError DeleteNcShank(int shankId) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - // Create backup of data stored on the NC and block file accesses - CmsError cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - return cmsError; - - // Delete database shank - DbNcShankModel deletedShank = toolsManager.DeleteShank(shankId); - bool startIsActive = false; - if (deletedShank.MagazineId != null) - { - startIsActive = true; - // Create backup of data stored on the NC and block file accesses - cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - { - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - return cmsError; - } - - // Update nc data - cmsError = UpdateNcShank(toolsManager); - } - - ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); - - return cmsError; - } - } - } - - private CmsError UpdateNcShank(NcToolManagerController toolsManager) - { - // Get mounted shanks - List shanks = toolsManager - .FindShanks() - .Where(x => x.MagazineId != null) - .Select(x => (NcShankModel)x) - .ToList(); - - // Update nc data - return numericalControl.TOOLS_WUpdateShanks(shanks); - } - - public DTONcFamilyModel AddFamily(DTONewNcFamilyModel family) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - return (DTONcFamilyModel)toolsManager.AddFamily(family); - } - } - - public CmsError UpdateFamily(int familyId, DTONewNcFamilyModel family, out DTONcFamilyModel newFamily) - { - newFamily = new DTONcFamilyModel(); - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - // Update db family - newFamily = (DTONcFamilyModel)toolsManager.UpdateFamily(familyId, family); - - // Create backup of data stored on the NC and block file accesses - CmsError cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - { - ManageErrorAndTransaction(cmsError, dbContextTransaction, true); - return cmsError; - } - - // Update Nc families - cmsError = UpdateNcFamily(toolsManager); - - ManageErrorAndTransaction(cmsError, dbContextTransaction, true); - - return cmsError; - } - } - } - - public CmsError DeleteNcFamily(DbNcFamilyModel family) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - // Create backup of data stored on the NC and block file accesses - CmsError cmsError = numericalControl.TOOLS_WStartEditData(); - if (cmsError.IsError()) - return cmsError; - - toolsManager.DeleteFamily(family); - // Update Nc families - cmsError = UpdateNcFamily(toolsManager); - - ManageErrorAndTransaction(cmsError, dbContextTransaction, true); - - return cmsError; - } - } - } - - private CmsError UpdateNcFamily(NcToolManagerController toolsManager) - { - // Get mounted shanks - List shanks = toolsManager - .FindShanks() - .Where(x => x.MagazineId != null) - .Select(x => (NcShankModel)x) - .ToList(); - - // Get tools - List tools = toolsManager - .FindTools() - .Select(x => (NcToolModel)x) - .Where(x => shanks.Any(y => y.ShankId == x.ShankId)) // Find only mounted tools - .ToList(); - - List families = toolsManager - .FindFamilies() - .Select(x => (NcFamilyModel)x) - .Where(x => tools.Any(y => y.FamilyId == x.FamilyId)) // Find only families of mounted tools - .ToList(); - - // Update nc families - return numericalControl.TOOLS_WUpdateFamilies(families); - } - - public CmsError UpdateMagazinePosition(DbNcMagazinePositionModel dbPos, DTONcMagazinePositionModel dtoPos, out DTONcMagazinePositionModel magPos) - { - magPos = new DTONcMagazinePositionModel(); - - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - using (DbContextTransaction dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) - { - // Create backup of data stored on the NC and block file accesses - CmsError cmsError = StartEditData(); - if (cmsError.IsError()) - { - ManageErrorAndTransaction(cmsError, dbContextTransaction, true); - return cmsError; - } - - // Update database data - magPos = (DTONcMagazinePositionModel)toolsManager.UpdatePosition(dbPos, dtoPos); - - // Get magazines positions - List positions = toolsManager.FindMagazinesPositions() - .Select(x => (NcMagazinePositionModel)x) - .ToList(); - // Update Nc data - cmsError = numericalControl.TOOLS_WUpdateMagazinePositions(positions); - - ManageErrorAndTransaction(cmsError, dbContextTransaction, true); - - return cmsError; - } - } - } - - private void ManageErrorAndTransaction(CmsError cmsError, DbContextTransaction dbContextTransaction, bool ncStartIsActive) - { - // Check cmsError, manage transaction and if startIsActive = true manage Nc backup created with TOOLS_WStartEditData() - if (cmsError.IsError()) - { - // Close transaction - dbContextTransaction.Rollback(); - // Restore backup - if (ncStartIsActive) - numericalControl.TOOLS_WRestoreBackup(); - } - else - { - // Close transaction - dbContextTransaction.Commit(); - // Stop editing ncData - if (ncStartIsActive) - numericalControl.TOOLS_WStopEditData(); - } - } - - public DTONcShankModel LoadIntoShank(DbNcToolModel tool, int shankId) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - return toolsManager.LoadToolIntoShank(tool, shankId); - } - } - - public DTONcShankModel UnloadFromShank(DbNcToolModel tool) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - return toolsManager.UnloadToolFromShank(tool); - } - } - - public CmsError SetupNcToolManager() - { - if (NcConfig.NcVendor != NC_VENDOR.SIEMENS) - { - CmsError cmsError = UpdateMagazinePositionConf(); - if (cmsError.IsError()) - return cmsError; - - cmsError = WriteOption(); - if (cmsError.IsError()) - return cmsError; - } - - return NO_ERROR; - } - - public CmsError UpdateMagazinePositionConf() - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - // Get database configured positions - List dbPositions = toolsManager.FindMagazinesPositions(); - if (dbPositions.Count <= 0) - { - CmsError cmsError = GetMagazineConfiguration(out List config); - if (cmsError.IsError()) - return cmsError; - - // Setup new positions list in order to be saved in the database - List fromConfigPosition = new List(); - foreach (var conf in config) - { - for (byte i = 1; i <= conf.MaxPositions; i++) - { - // Create new position - fromConfigPosition.Add(new DbNcMagazinePositionModel() - { - MagazineId = conf.Id, - PositionId = i, - Disabled = false, - Type = 0 - }); - } - } - - // Update database - toolsManager.SetupMagazinePositions(fromConfigPosition); - - return cmsError; - } - - return NO_ERROR; - } - } - - public CmsError WriteOption() - { - ToolManagerOptionsModel options = new ToolManagerOptionsModel(); - SupportFunctions.CopyProperties(ToolManagerConfig, options); - - return numericalControl.TOOLS_WOptions(options); - } - - public CmsError GetMagazineConfiguration(out List config) - { - config = new List(); - // Read configuration - return numericalControl.TOOLS_RMagazineConfig(ref config); - } - - public CmsError StartEditData() - { - return numericalControl.TOOLS_WStartEditData(); - } - - public CmsError StopEditData() - { - return numericalControl.TOOLS_WStopEditData(); - } - - public CmsError StartEditTooling(int magazineId) - { - return numericalControl.TOOLS_WStartEditTooling(magazineId); - } - - public CmsError StopEditTooling(int magazineId) - { - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - // Get mounted shanks - List shanks = toolsManager - .FindShanks() - .Where(x => x.MagazineId != null) - .Select(x => (NcShankModel)x) - .ToList(); - - CmsError cmsError = numericalControl.TOOLS_WUpdateShanks(shanks); - if (cmsError.IsError()) - return cmsError; - - // Get magazine positions - List positions = toolsManager.FindMagazinesPositions() - .Select(x => (NcMagazinePositionModel)x) - .ToList(); - cmsError = numericalControl.TOOLS_WUpdateMagazinePositions(positions); - if (cmsError.IsError()) - return cmsError; - - // Get tools - List tools = toolsManager - .FindTools() - .Select(x => (NcToolModel)x) - .Where(x => shanks.Any(y => y.ShankId == x.ShankId)) // Find only mounted tools - .ToList(); - - // Update tools - cmsError = numericalControl.TOOLS_WUpdateTools(tools); - if (cmsError.IsError()) - return cmsError; - - List families = toolsManager - .FindFamilies() - .Select(x => (NcFamilyModel)x) - .Where(x => tools.Any(y => y.FamilyId == x.FamilyId)) // Find only families of mounted tools - .ToList(); - // Update families - cmsError = numericalControl.TOOLS_WUpdateFamilies(families); - if (cmsError.IsError()) - return cmsError; - } - - return numericalControl.TOOLS_WStopEditTooling(magazineId); - } - - public CmsError GetUpdatedToolsData(out Dictionary updatedStatus, out Dictionary updatedLives) - { - updatedStatus = new Dictionary(); - updatedLives = new Dictionary(); - return numericalControl.TOOLS_RUpdatedToolsData(ref updatedStatus, ref updatedLives); - } - - public CmsError CheckIfShankCanFit(int magazineId, int positionId, DbNcShankModel shankToBeLoaded) - { - CmsError cmsError = GetMagazineConfiguration(out List magazinesConfig); - if (cmsError.IsError()) - return cmsError; - - NcMagazineConfigModel magConfig = magazinesConfig.Where(x => x.Id == magazineId).FirstOrDefault();// Get only current magazine config - - using (NcToolManagerController toolsManager = new NcToolManagerController()) - { - List magazineMountedTools = toolsManager.GetMountedTools(); - - // Check if there is a tool already mounted in magazineId and positionId - var toolAlreadyMounted = magazineMountedTools.Where(x => x.Shank.MagazineId == magazineId && x.Shank.PositionId == positionId).FirstOrDefault(); - if (toolAlreadyMounted != null) - return MAGAZINE_POSITION_OCCUPIED_ERROR; - - List families = toolsManager.FindFamiliesByShankId(shankToBeLoaded.ShankId); - - int maxRight = families.Max(x => x.RightSize); - int maxLeft = families.Max(x => x.LeftSize); - - // Check if tool can fit in the position considering other tools - foreach (var mountedTool in magazineMountedTools) - { - // Check if is not the same position && if is different check if the position is on the same magazine - if (mountedTool.Shank.PositionId != positionId && mountedTool.Shank.MagazineId == magazineId) - { - int decPosition, incPosition; - decPosition = incPosition = mountedTool.Shank.PositionId.Value; - // if it is a circular magazine move position before ( for left case ) or after ( right case ) the location you want to mount the tool - if (magConfig.Type != NC_MAGAZINE_TYPE.BOX_MAGAZINE) - { - decPosition = decPosition < positionId ? decPosition + magConfig.MaxPositions : decPosition; - incPosition = incPosition > positionId ? incPosition - magConfig.MaxPositions : incPosition; - } - - if (!(magConfig.Type == NC_MAGAZINE_TYPE.BOX_MAGAZINE && mountedTool.Shank.PositionId == 1)) - { - // Right check - // Calculate left space occupied (with mounted shank) - double leftMounted = decPosition - (mountedTool.Family.LeftSize / 2.0); - // Calculate right space needed (with shank to be loaded) - double rightToBeMount = positionId + (maxRight / 2.0); - if (leftMounted < rightToBeMount) - return MAGAZINE_POSITION_OCCUPIED_ERROR; - } - - if (!(magConfig.Type == NC_MAGAZINE_TYPE.BOX_MAGAZINE && mountedTool.Shank.PositionId == magConfig.MaxPositions)) - { - // Left check - // Calculate right space occupied (with mounted shank) - double rightMounted = incPosition + (mountedTool.Family.RightSize / 2.0); - // Calculate left space needed (with shank to be loaded) - double leftToBeMounted = positionId - (maxLeft / 2.0); - if (rightMounted > leftToBeMounted) - return MAGAZINE_POSITION_OCCUPIED_ERROR; - } - } - } - } - - return NO_ERROR; - } - - public CmsError GetNcMagazineStatus(out Dictionary status) - { - status = new Dictionary(); - return numericalControl.TOOLS_RMagazineStatus(ref status); - } - - #endregion Osai/Fanuc Tools - } + public CmsError SetActiveLanguage(CultureInfo language) + { + // Set to true power on data by id + return numericalControl.NC_WLanguage(language); + } + + + #endregion Write data + + #region Siemens Tools + + public CmsError AddTool(ref SiemensToolModel tool) + { + return numericalControl.TOOLS_WAddTool(ref tool); + } + + public CmsError UpdateTool(ref SiemensToolModel tool) + { + return numericalControl.TOOLS_WUpdateTool(ref tool); + } + + public CmsError AddFamily(string name, out FamilyModel family) + { + family = new FamilyModel() + { + Name = name + }; + + return numericalControl.TOOLS_WAddFamily(ref family); + } + + public CmsError AddShank(ref ShankModel shank) + { + return numericalControl.TOOLS_WAddShank(ref shank); + } + + public CmsError UpdateShank(ref ShankModel shank) + { + return numericalControl.TOOLS_WUpdateShank(ref shank); + } + + public CmsError UpdateFamilyName(string oldName, string newName) + { + return numericalControl.TOOLS_WUpdateFamilyData(oldName, newName); + } + + public CmsError UpdateMagazinePosition(PositionModel magazinePosition) + { + return numericalControl.TOOLS_WUpdatePosition(magazinePosition); + } + + public CmsError DeleteTool(int id) + { + return numericalControl.TOOLS_WDeleteTool(id); + } + + public CmsError DeleteShank(int id) + { + return numericalControl.TOOLS_WDeleteShank(id); + } + + public CmsError DeleteFamily(string name) + { + return numericalControl.TOOLS_WDeleteFamily(name); + } + + public CmsError AddEdge(int toolId, ref EdgeModel edge) + { + return numericalControl.TOOLS_WAddEdge(toolId, ref edge); + } + + public CmsError UpdateEdge(int toolId, ref EdgeModel edge) + { + return numericalControl.TOOLS_WUpdateEdge(toolId, ref edge); + } + + public CmsError DeleteEdge(int toolId, int edgeId) + { + return numericalControl.TOOLS_WDeleteEdge(toolId, edgeId); + } + + public CmsError GetMagazinePositionsAndTools(int magazineId, out List magazinePos) + { + magazinePos = new List(); + return numericalControl.TOOLS_RMagazineTools(magazineId, ref magazinePos); + } + + public CmsError GetNotInMagazinesTools(out List multiTools, out List tools) + { + multiTools = new List(); + tools = new List(); + return numericalControl.TOOLS_RAvailableTools(ref multiTools, ref tools); + } + + public CmsError LoadToolInMagazine(int magazineId, NewToolInMagazineModel newMag) + { + return numericalControl.TOOLS_WLoadToolInMagazine(magazineId, newMag); + } + + public CmsError UnloadToolInMagazine(int magazineId, int positionId) + { + return numericalControl.TOOLS_WUnloadToolFromMagazine(magazineId, positionId); + } + + public CmsError LoadTooolIntoShank(int shankId, int positionId, int toolId) + { + return numericalControl.TOOLS_WLoadToolIntoShank(shankId, positionId, toolId); + } + + public CmsError UnloadTooolFromShank(int shankId, int positionId) + { + return numericalControl.TOOLS_WUnloadToolFromShank(shankId, positionId); + } + + #endregion Siemens Tools + + // OSAI FANUC + + #region Osai/Fanuc Tools + + public CmsError GetToolsData(out List dtoTools) + { + dtoTools = new List(); + + CmsError cmsError = NO_ERROR; + + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + List tools = toolsManager.FindToolsWithDependencies(); + + foreach (DbNcToolModel tool in tools) + { + DTONcToolModel dtoTool = (DTONcToolModel)tool; + + cmsError = GetToolData(tool, ref dtoTool); + + dtoTools.Add(dtoTool); + } + + return cmsError; + } + } + + public CmsError GetToolData(DbNcToolModel tool, ref DTONcToolModel dtoTool) + { + dtoTool = (DTONcToolModel)tool; + + OffsetModel offset = new OffsetModel(); + CmsError cmsError = NO_ERROR; + + if (tool.OffsetId1 != null && tool.OffsetId1 != 0) + { + // Read first offset data + cmsError = numericalControl.TOOLS_ROffset((short)tool.OffsetId1, ref offset); + if (cmsError.IsError()) + return cmsError; + + dtoTool.Offset1 = offset; + } + if (tool.OffsetId2 != null && tool.OffsetId2 != 0) + { + // Read second offset data + cmsError = numericalControl.TOOLS_ROffset((short)tool.OffsetId2, ref offset); + if (cmsError.IsError()) + return cmsError; + + dtoTool.Offset2 = offset; + } + if (tool.OffsetId3 != null && tool.OffsetId3 != 0) + { + // Read third offset data + cmsError = numericalControl.TOOLS_ROffset((short)tool.OffsetId3, ref offset); + if (cmsError.IsError()) + return cmsError; + + dtoTool.Offset3 = offset; + } + + return cmsError; + } + + public DTONcToolModel AddTool(DTONewNcToolModel tool) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + DbNcToolModel ncTool = toolsManager.AddTool(tool); + + DTONcToolModel dtoTool = new DTONcToolModel(); + GetToolData(ncTool, ref dtoTool); + + return dtoTool; + } + } + + public CmsError UpdateTool(int toolId, DTONewNcToolModel newTool, out DTONcToolModel toolWithOffsets) + { + toolWithOffsets = new DTONcToolModel(); + + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + // Update database tool Tool + DbNcToolModel dbTool = toolsManager.UpdateTool(toolId, newTool); + + // Populate updated tool with offset data + CmsError cmsError = GetToolData(dbTool, ref toolWithOffsets); + if (cmsError.IsError()) + { + dbContextTransaction.Rollback(); + return cmsError; + } + + bool startIsActive = false; + + // Check if tool is loaded into a shank + if (dbTool.ShankId != null) + { + // Check if shank is loaded into magazine + DbNcShankModel shank = toolsManager.FindShankWithTools(dbTool.ShankId.Value); + if (shank.MagazineId != null) + { + startIsActive = true; + // Create backup of data stored on the NC and block file accesses + cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + { + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + return cmsError; + } + + // Update tool + cmsError = UpdateNcTools(toolsManager); + } + } + + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + + return NO_ERROR; + } + } + } + + public CmsError UpdateNcTools(NcToolManagerController toolsManager = null) + { + if (toolsManager == null) + toolsManager = new NcToolManagerController(); + + List shanks = toolsManager + .FindShanks() + .Where(x => x.MagazineId != null) + .Select(x => (NcShankModel)x) + .ToList(); + + // Get tools + List tools = toolsManager + .FindTools() + .Select(x => (NcToolModel)x) + .Where(x => shanks.Any(y => y.ShankId == x.ShankId)) // Find only mounted tools + .ToList(); + + // Update tools + return numericalControl.TOOLS_WUpdateTools(tools); + } + + public CmsError UpdateOffset(short offsetId, OffsetModel offsetData) + { + return numericalControl.TOOLS_WOffset(offsetId, offsetData); + } + + public CmsError UpdateToolOffsetId(int toolId, int positionId, int offsetId, out DTONcToolModel toolWithOffsets) + { + toolWithOffsets = new DTONcToolModel(); + + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + // Update database tool Tool + DbNcToolModel dbTool = toolsManager.UpdateToolOffset(toolId, positionId, offsetId); + + // Populate updated tool with offset data + CmsError cmsError = GetToolData(dbTool, ref toolWithOffsets); + if (cmsError.IsError()) + { + dbContextTransaction.Rollback(); + return cmsError; + } + + bool startIsActive = false; + + // Check if tool is loaded into a shank + if (dbTool.ShankId != null) + { + // Check if shank is loaded into magazine + DbNcShankModel shank = toolsManager.FindShankWithTools(dbTool.ShankId.Value); + if (shank.MagazineId != null) + { + startIsActive = true; + // Create backup of data stored on the NC and block file accesses + cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + { + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + return cmsError; + } + + // Update tool + cmsError = UpdateNcTools(toolsManager); + } + } + + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + + return NO_ERROR; + } + } + } + + public CmsError DeleteNcTool(DbNcToolModel tool) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + // Delete db tool + toolsManager.DeleteTool(tool.ToolId); + + bool startIsActive = false; + CmsError cmsError = NO_ERROR; + + if (tool.ShankId != null) + { + // If tool is mounted than update Nc data + DbNcShankModel shank = toolsManager.FindShankWithTools(tool.ShankId.Value); + if (shank.MagazineId != null) + { + startIsActive = true; + // Create backup of data stored on the NC and block file accesses + cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + { + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + return cmsError; + } + + // Update Nc data + cmsError = UpdateNcTools(toolsManager); + } + } + + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + + return cmsError; + } + } + } + + public CmsError UpdateShank(int shankId, DTONewNcShankModel dtoShank, out DTONcShankModel shank) + { + shank = new DTONcShankModel(); + + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + bool startIsActive = false; + CmsError cmsError = NO_ERROR; + + // Update db data + shank = (DTONcShankModel)toolsManager.UpdateShank(shankId, dtoShank); + if (shank.MagazineId != null) + { + startIsActive = true; + // Create backup of data stored on the NC and block file accesses + cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + { + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + return cmsError; + } + + // Update nc data + cmsError = UpdateNcShank(toolsManager); + } + + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + + return cmsError; + } + } + } + + public CmsError DeleteNcShank(int shankId) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + // Create backup of data stored on the NC and block file accesses + CmsError cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + return cmsError; + + // Delete database shank + DbNcShankModel deletedShank = toolsManager.DeleteShank(shankId); + bool startIsActive = false; + if (deletedShank.MagazineId != null) + { + startIsActive = true; + // Create backup of data stored on the NC and block file accesses + cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + { + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + return cmsError; + } + + // Update nc data + cmsError = UpdateNcShank(toolsManager); + } + + ManageErrorAndTransaction(cmsError, dbContextTransaction, startIsActive); + + return cmsError; + } + } + } + + private CmsError UpdateNcShank(NcToolManagerController toolsManager) + { + // Get mounted shanks + List shanks = toolsManager + .FindShanks() + .Where(x => x.MagazineId != null) + .Select(x => (NcShankModel)x) + .ToList(); + + // Update nc data + return numericalControl.TOOLS_WUpdateShanks(shanks); + } + + public DTONcFamilyModel AddFamily(DTONewNcFamilyModel family) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + return (DTONcFamilyModel)toolsManager.AddFamily(family); + } + } + + public CmsError UpdateFamily(int familyId, DTONewNcFamilyModel family, out DTONcFamilyModel newFamily) + { + newFamily = new DTONcFamilyModel(); + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + // Update db family + newFamily = (DTONcFamilyModel)toolsManager.UpdateFamily(familyId, family); + + // Create backup of data stored on the NC and block file accesses + CmsError cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + { + ManageErrorAndTransaction(cmsError, dbContextTransaction, true); + return cmsError; + } + + // Update Nc families + cmsError = UpdateNcFamily(toolsManager); + + ManageErrorAndTransaction(cmsError, dbContextTransaction, true); + + return cmsError; + } + } + } + + public CmsError DeleteNcFamily(int famId) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (var dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + // Create backup of data stored on the NC and block file accesses + CmsError cmsError = numericalControl.TOOLS_WStartEditData(); + if (cmsError.IsError()) + return cmsError; + + toolsManager.DeleteFamily(famId); + // Update Nc families + cmsError = UpdateNcFamily(toolsManager); + + ManageErrorAndTransaction(cmsError, dbContextTransaction, true); + + return cmsError; + } + } + } + + private CmsError UpdateNcFamily(NcToolManagerController toolsManager) + { + // Get mounted shanks + List shanks = toolsManager + .FindShanks() + .Where(x => x.MagazineId != null) + .Select(x => (NcShankModel)x) + .ToList(); + + // Get tools + List tools = toolsManager + .FindTools() + .Select(x => (NcToolModel)x) + .Where(x => shanks.Any(y => y.ShankId == x.ShankId)) // Find only mounted tools + .ToList(); + + List families = toolsManager + .FindFamilies() + .Select(x => (NcFamilyModel)x) + .Where(x => tools.Any(y => y.FamilyId == x.FamilyId)) // Find only families of mounted tools + .ToList(); + + // Update nc families + return numericalControl.TOOLS_WUpdateFamilies(families); + } + + public CmsError UpdateMagazinePosition(DbNcMagazinePositionModel dbPos, DTONcMagazinePositionModel dtoPos, out DTONcMagazinePositionModel magPos) + { + magPos = new DTONcMagazinePositionModel(); + + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + using (DbContextTransaction dbContextTransaction = toolsManager.dbCtx.Database.BeginTransaction()) + { + // Create backup of data stored on the NC and block file accesses + CmsError cmsError = StartEditData(); + if (cmsError.IsError()) + { + ManageErrorAndTransaction(cmsError, dbContextTransaction, true); + return cmsError; + } + + // Update database data + magPos = (DTONcMagazinePositionModel)toolsManager.UpdatePosition(dbPos, dtoPos); + + // Get magazines positions + List positions = toolsManager.FindMagazinesPositions() + .Select(x => (NcMagazinePositionModel)x) + .ToList(); + // Update Nc data + cmsError = numericalControl.TOOLS_WUpdateMagazinePositions(positions); + + ManageErrorAndTransaction(cmsError, dbContextTransaction, true); + + return cmsError; + } + } + } + + private void ManageErrorAndTransaction(CmsError cmsError, DbContextTransaction dbContextTransaction, bool ncStartIsActive) + { + // Check cmsError, manage transaction and if startIsActive = true manage Nc backup created with TOOLS_WStartEditData() + if (cmsError.IsError()) + { + // Close transaction + dbContextTransaction.Rollback(); + // Restore backup + if (ncStartIsActive) + numericalControl.TOOLS_WRestoreBackup(); + } + else + { + // Close transaction + dbContextTransaction.Commit(); + // Stop editing ncData + if (ncStartIsActive) + numericalControl.TOOLS_WStopEditData(); + } + } + + public DTONcShankModel LoadIntoShank(DbNcToolModel tool, int shankId) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + return toolsManager.LoadToolIntoShank(tool, shankId); + } + } + + public DTONcShankModel UnloadFromShank(DbNcToolModel tool) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + return toolsManager.UnloadToolFromShank(tool); + } + } + + public CmsError SetupNcToolManager() + { + if (NcConfig.NcVendor != NC_VENDOR.SIEMENS) + { + CmsError cmsError = UpdateMagazinePositionConf(); + if (cmsError.IsError()) + return cmsError; + + cmsError = WriteOption(); + if (cmsError.IsError()) + return cmsError; + } + + return NO_ERROR; + } + + public CmsError UpdateMagazinePositionConf() + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + // Get database configured positions + List dbPositions = toolsManager.FindMagazinesPositions(); + if (dbPositions.Count <= 0) + { + CmsError cmsError = GetMagazineConfiguration(out List config); + if (cmsError.IsError()) + return cmsError; + + // Setup new positions list in order to be saved in the database + List fromConfigPosition = new List(); + foreach (var conf in config) + { + for (byte i = 1; i <= conf.MaxPositions; i++) + { + // Create new position + fromConfigPosition.Add(new DbNcMagazinePositionModel() + { + MagazineId = conf.Id, + PositionId = i, + Disabled = false, + Type = 0 + }); + } + } + + // Update database + toolsManager.SetupMagazinePositions(fromConfigPosition); + + return cmsError; + } + + return NO_ERROR; + } + } + + public CmsError WriteOption() + { + ToolManagerOptionsModel options = new ToolManagerOptionsModel(); + SupportFunctions.CopyProperties(ToolManagerConfig, options); + + return numericalControl.TOOLS_WOptions(options); + } + + public CmsError GetMagazineConfiguration(out List config) + { + config = new List(); + // Read configuration + return numericalControl.TOOLS_RMagazineConfig(ref config); + } + + public CmsError StartEditData() + { + return numericalControl.TOOLS_WStartEditData(); + } + + public CmsError StopEditData() + { + return numericalControl.TOOLS_WStopEditData(); + } + + public CmsError StartEditTooling(int magazineId) + { + return numericalControl.TOOLS_WStartEditTooling(magazineId); + } + + public CmsError StopEditTooling(int magazineId) + { + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + // Get mounted shanks + List shanks = toolsManager + .FindShanks() + .Where(x => x.MagazineId != null) + .Select(x => (NcShankModel)x) + .ToList(); + + CmsError cmsError = numericalControl.TOOLS_WUpdateShanks(shanks); + if (cmsError.IsError()) + return cmsError; + + // Get magazine positions + List positions = toolsManager.FindMagazinesPositions() + .Select(x => (NcMagazinePositionModel)x) + .ToList(); + cmsError = numericalControl.TOOLS_WUpdateMagazinePositions(positions); + if (cmsError.IsError()) + return cmsError; + + // Get tools + List tools = toolsManager + .FindTools() + .Select(x => (NcToolModel)x) + .Where(x => shanks.Any(y => y.ShankId == x.ShankId)) // Find only mounted tools + .ToList(); + + // Update tools + cmsError = numericalControl.TOOLS_WUpdateTools(tools); + if (cmsError.IsError()) + return cmsError; + + List families = toolsManager + .FindFamilies() + .Select(x => (NcFamilyModel)x) + .Where(x => tools.Any(y => y.FamilyId == x.FamilyId)) // Find only families of mounted tools + .ToList(); + // Update families + cmsError = numericalControl.TOOLS_WUpdateFamilies(families); + if (cmsError.IsError()) + return cmsError; + } + + return numericalControl.TOOLS_WStopEditTooling(magazineId); + } + + public CmsError GetUpdatedToolsData(out Dictionary updatedStatus, out Dictionary updatedLives) + { + updatedStatus = new Dictionary(); + updatedLives = new Dictionary(); + return numericalControl.TOOLS_RUpdatedToolsData(ref updatedStatus, ref updatedLives); + } + + public CmsError CheckIfShankCanFit(int magazineId, int positionId, DbNcShankModel shankToBeLoaded) + { + CmsError cmsError = GetMagazineConfiguration(out List magazinesConfig); + if (cmsError.IsError()) + return cmsError; + + NcMagazineConfigModel magConfig = magazinesConfig.Where(x => x.Id == magazineId).FirstOrDefault();// Get only current magazine config + + using (NcToolManagerController toolsManager = new NcToolManagerController()) + { + List magazineMountedTools = toolsManager.GetMountedTools(); + + // Check if there is a tool already mounted in magazineId and positionId + var toolAlreadyMounted = magazineMountedTools.Where(x => x.Shank.MagazineId == magazineId && x.Shank.PositionId == positionId).FirstOrDefault(); + if (toolAlreadyMounted != null) + return MAGAZINE_POSITION_OCCUPIED_ERROR; + + List families = toolsManager.FindFamiliesByShankId(shankToBeLoaded.ShankId); + + int maxRight = families.Max(x => x.RightSize); + int maxLeft = families.Max(x => x.LeftSize); + + // Check if tool can fit in the position considering other tools + foreach (var mountedTool in magazineMountedTools) + { + // Check if is not the same position && if is different check if the position is on the same magazine + if (mountedTool.Shank.PositionId != positionId && mountedTool.Shank.MagazineId == magazineId) + { + int decPosition, incPosition; + decPosition = incPosition = mountedTool.Shank.PositionId.Value; + // if it is a circular magazine move position before ( for left case ) or after ( right case ) the location you want to mount the tool + if (magConfig.Type != NC_MAGAZINE_TYPE.BOX_MAGAZINE) + { + decPosition = decPosition < positionId ? decPosition + magConfig.MaxPositions : decPosition; + incPosition = incPosition > positionId ? incPosition - magConfig.MaxPositions : incPosition; + } + + if (!(magConfig.Type == NC_MAGAZINE_TYPE.BOX_MAGAZINE && mountedTool.Shank.PositionId == 1)) + { + // Right check + // Calculate left space occupied (with mounted shank) + double leftMounted = decPosition - (mountedTool.Family.LeftSize / 2.0); + // Calculate right space needed (with shank to be loaded) + double rightToBeMount = positionId + (maxRight / 2.0); + if (leftMounted < rightToBeMount) + return MAGAZINE_POSITION_OCCUPIED_ERROR; + } + + if (!(magConfig.Type == NC_MAGAZINE_TYPE.BOX_MAGAZINE && mountedTool.Shank.PositionId == magConfig.MaxPositions)) + { + // Left check + // Calculate right space occupied (with mounted shank) + double rightMounted = incPosition + (mountedTool.Family.RightSize / 2.0); + // Calculate left space needed (with shank to be loaded) + double leftToBeMounted = positionId - (maxLeft / 2.0); + if (rightMounted > leftToBeMounted) + return MAGAZINE_POSITION_OCCUPIED_ERROR; + } + } + } + } + + return NO_ERROR; + } + + public CmsError GetNcMagazineStatus(out Dictionary status) + { + status = new Dictionary(); + return numericalControl.TOOLS_RMagazineStatus(ref status); + } + + #endregion Osai/Fanuc Tools + } } \ No newline at end of file diff --git a/Step/Controllers/WebApi/NcToolManagerController.cs b/Step/Controllers/WebApi/NcToolManagerController.cs index a5527679..f24213a4 100644 --- a/Step/Controllers/WebApi/NcToolManagerController.cs +++ b/Step/Controllers/WebApi/NcToolManagerController.cs @@ -219,9 +219,12 @@ namespace Step.Controllers.WebApi if (!ToolManagerConfig.FamilyOpt) { DbNcFamilyModel fam = toolsManager.FindFamily(tool.FamilyId); - cmsError = ncHandler.DeleteNcFamily(fam); - if (cmsError.IsError()) - return BadRequest(cmsError.localizationKey); + if(fam.Tools.Count() == 1) + { + cmsError = ncHandler.DeleteNcFamily(tool.FamilyId); + if (cmsError.IsError()) + return BadRequest(cmsError.localizationKey); + } } cmsError = ncHandler.DeleteNcTool(tool); @@ -431,7 +434,7 @@ namespace Step.Controllers.WebApi return BadRequest(cmsError.localizationKey); //Delete - cmsError = ncHandler.DeleteNcFamily(family); + cmsError = ncHandler.DeleteNcFamily(familyId); if (cmsError.IsError()) return BadRequest(cmsError.localizationKey); diff --git a/Step/Listeners/SignalR/SignalRListener.cs b/Step/Listeners/SignalR/SignalRListener.cs index edfd921a..2f6e6d0c 100644 --- a/Step/Listeners/SignalR/SignalRListener.cs +++ b/Step/Listeners/SignalR/SignalRListener.cs @@ -209,7 +209,7 @@ namespace Step.Listeners.SignalR if (!LastProgramData.Equals(programData)) { LastProgramData = programData as DTOActiveProgramDataModel; - + context.Clients.Group("ncData").activeProgramData(programData); } } @@ -273,7 +273,7 @@ namespace Step.Listeners.SignalR // Send active program data context.Clients.Group("ncData").activeProgramData(LastProgramData); // Send magazine is active data - context.Clients.Group("ncData").magazineIsActive(LastNcMagazineIsActive); + context.Clients.Group("ncData").magazineIsActive(LastNcMagazineIsActive); Debug.WriteLine(string.Format("{0} {1} Broadcast..completed", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"), DateTime.Now.Millisecond)); Monitor.Exit(_broadcastlock);