Compare commits

...

66 Commits

Author SHA1 Message Date
Samuele Locatelli 8542b6c565 Merge branch 'develop' into new/dirSaveMgt 2020-09-12 17:25:04 +02:00
Samuele Locatelli 3c65bced53 fix letter case per xsd 2020-09-12 17:24:52 +02:00
Samuele Locatelli 95d29cc716 Aggunta files di config XML/XSD 2020-09-12 17:19:55 +02:00
Samuele Locatelli bcde0c8d86 Completato inserimento metodi x M156 2020-09-12 17:09:11 +02:00
Samuele Locatelli 76deabb93b Aggiunta preliminare metodi x gestione M156 2020-09-12 16:41:29 +02:00
Samuele Locatelli bfc99fc826 Merge branch 'develop' into new/dirSaveMgt 2020-09-10 17:26:53 +02:00
Samuele Locatelli cb28589f79 Gestione calcolo modifica parametri ricette + fix livedata save (tested) 2020-09-10 17:26:37 +02:00
Samuele Locatelli 8cae6edff2 start new rel 2020-09-10 15:33:45 +02:00
Samuele Locatelli 34a71b5784 Merge branch 'develop' into new/dirSaveMgt 2020-09-10 13:15:53 +02:00
Samuele Locatelli 455ddd660c new rel x SIM 2020-09-10 13:15:42 +02:00
Samuele Locatelli bbada3a929 update x fix calcolo tempo ciclo, calcolo durata, inizio lotto... 2020-09-10 13:15:29 +02:00
Samuele Locatelli 0a00ba3eae Merge remote-tracking branch 'origin/develop' into new/dirSaveMgt 2020-09-10 12:29:13 +02:00
Samuele Locatelli 7a3a068908 new rel 2020-09-10 12:21:13 +02:00
= 2a3de715da Merge remote-tracking branch 'CMS/develop' into develop 2020-09-10 10:12:47 +02:00
= 002b235d76 fix dashboar 2020-09-10 10:12:14 +02:00
= b135f017d5 modali da gantt 2020-09-10 09:52:02 +02:00
Samuele Locatelli 32e0c34945 rel ver x prod 2020-09-10 07:58:00 +02:00
Samuele Locatelli f32d0c0daf Merge remote-tracking branch 'origin/develop' into new/dirSaveMgt 2020-09-10 07:56:57 +02:00
NICOLA CARMINATI 014f63bd2b Added folder / file Manager to the Client 2020-09-09 18:03:56 +02:00
= a6e58365dd recipe note.. preview on save 2020-09-09 16:55:03 +02:00
= 7481d3bf28 print ricetta 2020-09-09 16:43:48 +02:00
= c036cd30e2 Merge remote-tracking branch 'CMS/develop' into develop 2020-09-09 15:51:52 +02:00
= 682efe1928 enabled fields visibility 2020-09-09 12:16:04 +02:00
= d511b49c64 enabled fields visibility 2020-09-09 12:15:54 +02:00
Samuele Locatelli bb7ef476ed new rel 2020-09-09 09:58:42 +02:00
Samuele Locatelli 393da8156b Aggiunto metodo x fornire note ad UI 2020-09-09 09:58:31 +02:00
Samuele Locatelli 575b74c676 Merge remote-tracking branch 'origin/develop' into new/dirSaveMgt 2020-09-09 08:16:52 +02:00
Samuele Locatelli 9f27a7b5ce Impostato default dirm da serverConfig/sharedPath 2020-09-09 08:16:43 +02:00
NICOLA CARMINATI 12ee118e72 Fix OnScreenKeyboard Option 2020-09-08 18:08:02 +02:00
= 00994f993b fix number 2020-09-08 17:31:50 +02:00
= 581c6d44fe fix sovrapposizione gant boxes 2020-09-08 15:04:32 +02:00
Samuele Locatelli a75edd650e start new rel 2020-09-08 14:56:50 +02:00
= eea8a2fa5d fix apertura blocchetti da categorie 2020-09-08 12:50:59 +02:00
= 14c9fb628b warmers 2020-09-08 12:40:25 +02:00
= 18530ea7b3 fix combo 2020-09-08 10:48:46 +02:00
= 04447282ea timeline gannt 2020-09-08 10:23:52 +02:00
= 582a4455e0 Merge remote-tracking branch 'CMS/develop' into develop 2020-09-08 10:09:28 +02:00
= 43fa8448d2 fix disegno stampo 2020-09-08 10:09:18 +02:00
Samuele Locatelli a6df5719f0 Merge remote-tracking branch 'origin/develop' into feature/prodMgmt 2020-09-08 09:49:07 +02:00
Samuele Locatelli d23f6fd4c5 new rel 2020-09-08 09:48:58 +02:00
= 91a554d616 utilities 2020-09-08 09:43:12 +02:00
= e96d97890f cancel della ricetta 2020-09-08 09:37:52 +02:00
= 42186c9509 fix remainging time negativo 2020-09-08 09:19:51 +02:00
= b0391855f5 fix numeric 2020-09-08 08:54:52 +02:00
= 305e2220f8 valueact 2020-09-08 08:48:31 +02:00
= a5d2323dea fix combo status 2020-09-08 08:41:17 +02:00
= 95db7bbdd9 Merge remote-tracking branch 'CMS/develop' into develop 2020-09-08 08:35:44 +02:00
= 345918cdaf valueAct 2020-09-08 08:35:19 +02:00
Samuele Locatelli 8bc9fc97c8 new rel 2020-09-07 13:55:09 +02:00
Samuele Locatelli cd5ea6c6cc Merge remote-tracking branch 'origin/develop' into feature/prodMgmt 2020-09-07 13:53:07 +02:00
= fb91158ee6 fix gant print & slider. 2020-09-07 12:52:53 +02:00
= 7b724b070f slider 2020-09-07 11:16:11 +02:00
= 7bb8602595 Merge remote-tracking branch 'CMS/develop' into develop 2020-09-07 10:32:55 +02:00
Samuele Locatelli 02b1b3b4c6 Merge branch 'feature/prodMgmt' into develop 2020-09-04 19:25:10 +02:00
Samuele Locatelli f499cdc203 new rel intermedia 71 2020-09-04 19:24:32 +02:00
Samuele Locatelli fa9db02e0e Aggiornamento gestione moduli x avere categorie + sub 2020-09-04 19:24:18 +02:00
Samuele Locatelli 136f3c40ff Update xml e validatore modBlock 2020-09-04 19:23:57 +02:00
= 0e02b0ca3f fixes 2020-09-04 17:14:15 +02:00
= 913834a34d Merge remote-tracking branch 'CMS/develop' into develop 2020-09-04 15:14:54 +02:00
= 6b0d5e2c03 fix generali e gestione produzione 2020-09-04 15:14:32 +02:00
Samuele Locatelli 3ead2b7283 Merge branch 'feature/prodMgmt' into develop 2020-09-04 11:52:21 +02:00
Samuele Locatelli 66ba44868e Update x nuova gest codici errore in core library 2020-09-04 11:46:30 +02:00
= 9be02d4666 dashboard & toggle.. 2020-09-02 17:52:54 +02:00
= 2f2fc02730 clock & dashboard 2020-09-02 15:44:10 +02:00
= cccd8f2c36 gantt printing 2020-09-02 15:15:41 +02:00
= 71a1333c12 gantt print 2020-09-01 16:52:24 +02:00
101 changed files with 2408 additions and 697 deletions
+92 -7
View File
@@ -38,12 +38,12 @@ namespace Active_Client.Browser_Tools
private static readonly string[] _validExtensions = {".json", ".rcp", ".tpl" };
//private static readonly string[] _validExtensions = { "", ".txt", ".cnc", ".cn", ".cno", ".ini", ".mpf", ".spf", ".tap", ".anc", ".iso" };
private static readonly string[] _validImages = { ".jpg", ".jpeg", ".png" };
private static readonly string[] _validImages = { ".jpg", ".jpeg", ".png", ".svg" };
private static string jobPath = "";
private static Dictionary<string, IntPtr> _editorOpened = new Dictionary<string, IntPtr>();
private static EditorVar _currentEditorObject = new EditorVar();
public static string RECENT_FOLDER_KEY = "RECENT";
private const string THERMO_RECIPE_PATH = @"C:\CMS\Recipes";
private const string THERMO_RECIPE_PATH = @"C:\CMS\Recipe";
public static FileSystemWatcher watcher = null;
public static DateTime _lastTimeFileWatcherEventRaised = DateTime.Now;
@@ -80,6 +80,11 @@ namespace Active_Client.Browser_Tools
AddFunction("getFileList").Execute += getFileList;
AddFunction("getProgramInfo").Execute += getProgramInfo;
AddFunction("editProgram").Execute += editProgram;
AddFunction("deleteFile").Execute += deleteFile;
AddFunction("deleteFolder").Execute += deleteFolder;
AddFunction("createFolder").Execute += createFolder;
AddFunction("uploadAndActivateProgram").Execute += uploadAndActivateProgram;
AddFunction("uploadAndAddToQueue").Execute += uploadAndAddToQueue;
@@ -360,6 +365,7 @@ namespace Active_Client.Browser_Tools
List<Drive> drivelist = new List<Drive>();
// USB & HD Drives
/*
foreach (var drive in DriveInfo.GetDrives())
{
if (drive.IsReady)
@@ -376,7 +382,7 @@ namespace Active_Client.Browser_Tools
}
}
}
// Desktop folder
drivelist.Add(new Drive()
{
@@ -384,6 +390,7 @@ namespace Active_Client.Browser_Tools
Path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\",
Type = "SPFO"
});
*/
if (Directory.Exists(THERMO_RECIPE_PATH))
{
@@ -394,7 +401,7 @@ namespace Active_Client.Browser_Tools
Type = "SPFO"
});
}
/*
try
{
// Network Folders
@@ -416,7 +423,7 @@ namespace Active_Client.Browser_Tools
catch (Exception ex)
{
}
}*/
e.SetReturnValue(JsonConvert.SerializeObject(drivelist));
}
@@ -485,6 +492,85 @@ namespace Active_Client.Browser_Tools
e.SetReturnValue(JsonConvert.SerializeObject(filelist));
}
public void deleteFile(object sender, CfrV8HandlerExecuteEventArgs e)
{
if (e.Arguments.Count() == 0)
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("error_arguments_not_ok")));
return;
}
// Get path
string p = e.Arguments[0].StringValue;
FileAttributes attr = File.GetAttributes(p);
if (!File.Exists(p) || attr.HasFlag(FileAttributes.Directory))
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("file_not_found")));
return;
}
if (attr.HasFlag(FileAttributes.ReadOnly))
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("file_not_editable")));
return;
}
try
{
File.Delete(p);
}
catch(Exception ex)
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("cannot_delete_file")));
}
}
public void deleteFolder(object sender, CfrV8HandlerExecuteEventArgs e)
{
if (e.Arguments.Count() == 0)
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("error_arguments_not_ok")));
return;
}
// Get path
string p = e.Arguments[0].StringValue;
FileAttributes attr = File.GetAttributes(p);
if (!Directory.Exists(p) || !attr.HasFlag(FileAttributes.Directory))
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("directory_not_found")));
return;
}
if (attr.HasFlag(FileAttributes.ReadOnly))
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("directory_not_editable")));
return;
}
try
{
Directory.Delete(p,true);
}
catch (Exception ex)
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("cannot_delete_directory")));
}
}
public void createFolder(object sender, CfrV8HandlerExecuteEventArgs e)
{
if (e.Arguments.Count() == 0)
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("error_arguments_not_ok")));
return;
}
string path = e.Arguments[0].StringValue;
try
{
Directory.CreateDirectory(path);
}
catch (Exception ex)
{
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("cannot_delete_directory")));
}
}
// Upload and activate the program
public async void uploadAndActivateProgram(object sender, CfrV8HandlerExecuteEventArgs e)
{
@@ -671,8 +757,7 @@ namespace Active_Client.Browser_Tools
// Read info of a file
public void getProgramInfo(object sender, CfrV8HandlerExecuteEventArgs e)
{
string line, imagePath, imageDirectory;
int counter = 0;
string imagePath, imageDirectory;
if (e.Arguments.Count() == 0)
{
+2 -2
View File
@@ -165,7 +165,7 @@ namespace Active_Client.View
public void sendClose()
{
//Close Virtual Keyboard Runtime
if (Config.ClientConfig.ShowVirtualKeyboard && Environment.OSVersion.Version.Major < 10)
if (Config.ClientConfig.ShowVirtualKeyboard)
NcWindow.closeVirtualKeyboard();
//Close the NC HMI && Stop Following Nc
@@ -259,7 +259,7 @@ namespace Active_Client.View
Browser.DisplayHandler.OnConsoleMessage += BrowserConsoleMessage;
Browser.DownloadHandler.OnBeforeDownload += BeforeDownload;
//Filter only < Win_10 Platform
if (Config.ClientConfig.ShowVirtualKeyboard && Environment.OSVersion.Version.Major < 10)
if (Config.ClientConfig.ShowVirtualKeyboard)
ChromiumWebBrowser.RemoteProcessCreated += (e) => { e.RenderProcessHandler.OnFocusedNodeChanged += BrowserNodeChanged; };
}
+2 -2
View File
@@ -1047,7 +1047,7 @@ namespace Active_Client.View
if (!IsIconic(MainViewHandle))
{
//Show Virtual keyboard
if (Config.ClientConfig.ShowVirtualKeyboard && Environment.OSVersion.Version.Major < 10)
if (Config.ClientConfig.ShowVirtualKeyboard)
reOpenVirtualKeyboard();
}
}
@@ -1069,7 +1069,7 @@ namespace Active_Client.View
//SetForegroundWindow(hwnd);
//Hide Virtual keyboard
if (Config.ClientConfig.ShowVirtualKeyboard && Environment.OSVersion.Version.Major < 10 && KeyboardPID != 0 && ActualPID != KeyboardPID)
if (Config.ClientConfig.ShowVirtualKeyboard && KeyboardPID != 0 && ActualPID != KeyboardPID)
closeVirtualKeyboard();
}
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="inputsOperator">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="realValueModal" type="valuesType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="showValModal" type="showValType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="buttonsListModal" type="buttonsType" minOccurs="0" maxOccurs="unbounded"/>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:complexType name="valuesType">
<xs:all>
<xs:element name="id" minOccurs='1' maxOccurs='1'/>
<xs:element name="title" type="translatedText" minOccurs='1' maxOccurs='1'/>
</xs:all>
</xs:complexType>
<xs:complexType name="showValType">
<xs:all>
<xs:element name="id" minOccurs='1' maxOccurs='1'/>
<xs:element name="title" type="translatedText" minOccurs='1' maxOccurs='1'/>
<xs:element name="buttons">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="button">
<xs:complexType>
<xs:all>
<xs:element name="value" minOccurs="1" maxOccurs="1" type="xs:int"></xs:element>
<xs:element name="title" type="translatedText" minOccurs='1' maxOccurs='1'/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
<xs:complexType name="buttonsType">
<xs:all>
<xs:element name="id" minOccurs='1' maxOccurs='1'/>
<xs:element name="title" type="translatedText" minOccurs='1' maxOccurs='1'/>
<xs:element name="buttons">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="button">
<xs:complexType>
<xs:all>
<xs:element name="value" minOccurs="1" maxOccurs="1" type="xs:int"></xs:element>
<xs:element name="title" type="translatedText" minOccurs='1' maxOccurs='1'/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
<!-- Translations field Type -->
<xs:complexType name="translatedText">
<xs:sequence>
<xs:element name="lang" type="langType" minOccurs="0" maxOccurs="unbounded">
</xs:element>
</xs:sequence>
</xs:complexType>
<!-- lang field -->
<xs:complexType name="langType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="langKey" use="required" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<inputsOperator>
<buttonsListModal>
<id>1</id>
<title>
<lang langKey="it">Hai rimosso manualmente anche il pezzo lavorato ?</lang>
<lang langKey="en">Have you also manually removed the workpiece ?</lang>
</title>
<buttons>
<button>
<value>0</value>
<title>
<lang langKey="it">No</lang>
<lang langKey="en">No</lang>
</title>
</button>
<button>
<value>1</value>
<title>
<lang langKey="it">Si</lang>
<lang langKey="en">Yes</lang>
</title>
</button>
</buttons>
</buttonsListModal>
<realValueModal>
<id>10</id>
<title>
<lang langKey="en">External water</lang>
<lang langKey="it">Acqua esterna</lang>
</title>
</realValueModal>
<realValueModal>
<id>11</id>
<title>
<lang langKey="en">External water</lang>
<lang langKey="it">Acqua esterna</lang>
</title>
</realValueModal>
<buttonsListModal>
<id>6</id>
<title>
<lang langKey="en">External water</lang>
<lang langKey="it">Acqua esterna</lang>
</title>
<buttons>
<button>
<value>3</value>
<title>
<lang langKey="en">External water</lang>
<lang langKey="it">Acqua esterna</lang>
</title>
</button>
<button>
<value>4</value>
<title>
<lang langKey="en">External water</lang>
<lang langKey="it">Acqua esterna</lang>
</title>
</button>
</buttons>
</buttonsListModal>
</inputsOperator>
@@ -71,6 +71,9 @@
<idParam>80</idParam>
<showDelay>true</showDelay>
<priority>3</priority>
<category>Pyrometer</category>
<subCategory_1>Pyrometer</subCategory_1>
<subCategory_2></subCategory_2>
</block>
<block>
<id>9</id>
@@ -116,6 +119,9 @@
<idParam>99</idParam>
<showDelay>true</showDelay>
<priority>2</priority>
<category>Drawing</category>
<subCategory_1></subCategory_1>
<subCategory_2></subCategory_2>
</block>
<block>
<id>14</id>
@@ -152,6 +158,9 @@
<idParam>139</idParam>
<showDelay>true</showDelay>
<priority>4</priority>
<category>Cooling</category>
<subCategory_1>Pyrometer</subCategory_1>
<subCategory_2></subCategory_2>
</block>
<block>
<id>19</id>
@@ -16,6 +16,9 @@
<xs:element name="priority" type="xs:int" />
<xs:element name="scaleFactor" type="xs:int" minOccurs="0" />
<xs:element name="numDec" type="xs:int" minOccurs="0" />
<xs:element name="category" type="param_type" minOccurs="0" />
<xs:element name="subCategory_1" type="xs:string" minOccurs="0" />
<xs:element name="subCategory_2" type="xs:string" minOccurs="0" />
</xs:all>
</xs:complexType>
</xs:element>
@@ -23,6 +26,23 @@
</xs:complexType>
</xs:element>
<!-- Head Type -->
<xs:simpleType name="param_type" final="restriction">
<xs:restriction base="xs:string">
<xs:enumeration value="General" />
<xs:enumeration value="Positions" />
<xs:enumeration value="Cycle"/>
<xs:enumeration value="Heats"/>
<xs:enumeration value="Pyrometer"/>
<xs:enumeration value="Drawing"/>
<xs:enumeration value="UpperPlate"/>
<xs:enumeration value="Cooling"/>
<xs:enumeration value="Vacuum"/>
<xs:enumeration value="Extraction"/>
<xs:enumeration value="Options"/>
</xs:restriction>
</xs:simpleType>
<!-- Language Type -->
<xs:complexType name="langType">
<xs:simpleContent>
+1 -1
View File
@@ -6,7 +6,7 @@
<ncIpAddress>192.168.0.102</ncIpAddress>
<ncPort>102</ncPort>
<machineModel>Thermo 2020</machineModel>
<sharedPath>C:\PartPrg\</sharedPath>
<sharedPath>C:\CMS\Recipes\</sharedPath>
<sharedName>//PARTPRG:/</sharedName>
<installationDate>01/06/2020</installationDate>
<mgiOption>false</mgiOption>
+1 -2
View File
@@ -51,10 +51,9 @@ namespace Thermo.Active.Config
public static List<ScadaSchemaModel> SubscribedScada = new List<ScadaSchemaModel>();
public static List<string> MacrosConfig;
public static List<InputOperatorConfigModel> InputsOperatorConfig;
public static string CMSMainProgramContent;
// Thermo
public static List<ThermoProdConfigModel> ThermoProdConfig;
public static List<RecipeConfigModel> RecipeConfig;
+46 -2
View File
@@ -41,6 +41,7 @@ namespace Thermo.Active.Config
// ReadCMSConnectConfig();
ReadMacros();
ReadScadaFile();
ReadM156();
}
catch (XmlException ex)
{
@@ -254,6 +255,47 @@ namespace Thermo.Active.Config
}
}
private static void ReadM156()
{
// Get Areas file handler
XDocument xmlConfigFile = GetXmlHandlerWithValidator(M156_CONFIG_SCHEMA_PATH, M156_CONFIG_PATH);
InputsOperatorConfig = xmlConfigFile
.Descendants("inputsOperator")
.Elements()
.Select(x => new InputOperatorConfigModel()
{
Id = Convert.ToInt32(x.Element("id").Value),
Messages = x.Element("title").Elements().ToDictionary( // Read localized names and convert into a dictionary
y => y.Attribute("langKey").Value, y => y.Value
),
Buttons = x.Element("buttons")?.Elements().ToDictionary( // Read buttons data and convert into a dictionary
y => Convert.ToByte(y.Element("value").Value),
y => y.Element("title").Elements().ToDictionary( // Read localized names and convert into a dictionary
z => z.Attribute("langKey").Value,
z => z.Value
)
),
Type = GetInputOperatoType(x.Name.ToString())
})
.ToList();
}
public static string GetInputOperatoType(string tagName)
{
switch (tagName)
{
case "modalValue":
return "REAL";
case "buttonsListModal":
return "MULTIPLE_BUTTONS";
case "showValModal":
return "SHOW_VAL";
default:
return "REAL";
}
}
#region Read config from file from configuration
@@ -628,7 +670,6 @@ namespace Thermo.Active.Config
NumDec = Convert.ToInt32(x.Element("numDec").Value),
MinVal = Convert.ToInt32(x.Element("minVal").Value),
MaxVal = Convert.ToInt32(x.Element("maxVal").Value),
})
.ToList();
}
@@ -654,7 +695,10 @@ namespace Thermo.Active.Config
Priority = Convert.ToInt16(x.Element("priority").Value),
// attributi presi da default se NON presenti...
ScaleFactor = x.Element("scaleFactor") != null ? Convert.ToInt16(x.Element("scaleFactor").Value) : 1000,
NumDec = x.Element("numDec") != null ? Convert.ToInt16(x.Element("numDec").Value) : 1
NumDec = x.Element("numDec") != null ? Convert.ToInt16(x.Element("numDec").Value) : 1,
Category = x.Element("category") != null ? x.Element("category").Value : "",
SubCategory_1 = x.Element("subCategory_1") != null ? x.Element("subCategory_1").Value : "",
SubCategory_2 = x.Element("subCategory_2") != null ? x.Element("subCategory_2").Value : ""
})
.ToList();
}
@@ -51,6 +51,9 @@
<Content Include="Config\axesConfig.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\inputOperatorConfig.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\macrosConfig.xml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -200,6 +203,10 @@
</ItemGroup>
<ItemGroup />
<ItemGroup>
<EmbeddedResource Include="Config\inputOperatorConfigValidator.xsd">
<SubType>Designer</SubType>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Include="Config\Recipes\template.tpl">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
+48
View File
@@ -957,6 +957,54 @@ public static class ThreadsFunctions
ncAdapter.Dispose();
}
}
public static void ReadMComandsData()
{
NcAdapter ncAdapter = new NcAdapter();
Stopwatch sw = new Stopwatch();
try
{
// Try connection
CmsError libraryError = ncAdapter.Connect();
if (libraryError.errorCode != 0)
ManageLibraryError(libraryError);
while (true)
{
sw.Restart();
if (ncAdapter.numericalControl.NC_IsConnected())
{
// solo x S7Net...
if (NcConfig.NcVendor == NC_VENDOR.S7NET)
{
libraryError = ncAdapter.GetM156Data(out List<DTOM156InputModel> m156Data);
if (libraryError.IsError())
ManageLibraryError(libraryError);
else
MessageServices.Current.Publish(SEND_M156_DATA, null, m156Data);
}
}
else
RestoreConnection();
sw.Stop();
//Update thread timer
UpdateStat(MethodBase.GetCurrentMethod().Name, sw.ElapsedMilliseconds);
// Wait
Thread.Sleep(CalcSleepTime(200, (int)sw.ElapsedMilliseconds));
}
}
catch (ThreadAbortException)
{
ncAdapter.Dispose();
}
}
public static void ReadM154Data()
{
NcAdapter ncAdapter = new NcAdapter();
+2
View File
@@ -35,7 +35,9 @@ namespace Thermo.Active.Core
ThreadsFunctions.ReadAreaData,
ThreadsFunctions.ReadModulesData,
ThreadsFunctions.ReadScadaData,
ThreadsFunctions.ReadMComandsData,
ThreadsFunctions.ReadM154Data // levare?
};
private static Action ThreadSetupCmsConnect = ThreadsFunctions.SetupCmsConnect;
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Thermo.Active.Model.ConfigModels
{
public class InputOperatorConfigModel
{
public int Id { get; set; }
public Dictionary<string, string> Messages { get; set; }
public Dictionary<byte, Dictionary<string, string>> Buttons { get; set; }
public string Type { get; set; }
}
}
@@ -14,5 +14,8 @@ namespace Thermo.Active.Model.ConfigModels
public int Priority { get; set; }
public int ScaleFactor { get; set; } = 1000;
public int NumDec { get; set; } = 1;
public string Category { get; set; }
public string SubCategory_1 { get; set; }
public string SubCategory_2 { get; set; }
}
}
+6 -3
View File
@@ -222,9 +222,8 @@ namespace Thermo.Active.Model
public const int numRecProdPanelGraph = 30;
public const string CONFIG_DIRECTORY = "Config\\";
public const string RECIPE_DIRECTORY = "Recipes\\";
public const string RECIPE_TEMPLATE_PATH = CONFIG_DIRECTORY + RECIPE_DIRECTORY + "template.tpl";
public const string LIVE_RECIPE_PATH = TEMP_FOLDER + RECIPE_DIRECTORY + "current.rcp";
public const string RECIPE_TEMPLATE_PATH = CONFIG_DIRECTORY + "Recipes\\template.tpl";
public const string LIVE_RECIPE_PATH = TEMP_FOLDER + "Recipes\\current.rcp";
public const string RESOURCE_DIRECTORY = @"Thermo.Active.Config.Config.";
public const string SERVER_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + @"serverConfigValidator.xsd";
public const string SERVER_CONFIG_PATH = CONFIG_DIRECTORY + "serverConfig.xml";
@@ -232,6 +231,9 @@ namespace Thermo.Active.Model
public const string AREAS_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + "areasConfigValidator.xsd";
public const string AREAS_CONFIG_PATH = CONFIG_DIRECTORY + "areasConfig.xml";
public const string M156_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + "inputOperatorConfigValidator.xsd";
public const string M156_CONFIG_PATH = CONFIG_DIRECTORY + "inputOperatorConfig.xml";
public const string MAINTENANCES_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + "maintenancesConfigValidator.xsd";
public const string CUSTOMER_CONTACTS_CONFIG_SCHEMA_PATH = RESOURCE_DIRECTORY + "customerContactConfigValidator.xsd";
public const string MAINTENANCES_CONFIG_PATH = CONFIG_DIRECTORY + "maintenancesConfig.xml";
@@ -309,6 +311,7 @@ namespace Thermo.Active.Model
public const string SEND_ACTIVE_PROGRAM_DATA = "SEND_ACTIVE_PROGRAM_DATA";
public const string SEND_QUEUE_DATA = "SEND_QUEUE_DATA";
public const string SEND_M155_DATA = "SEND_M155_DATA";
public const string SEND_M156_DATA = "SEND_M156_DATA";
public const string SEND_SCADA_DATA = "SEND_SCADA_DATA";
// MVVM Messages for Thermo active specs
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static CMS_CORE_Library.Models.DataStructures;
namespace Thermo.Active.Model.DTOModels
{
public class DTOM156InputModel : M156InputIsNeededModel
{
public Dictionary<byte, string> Buttons;
public string Type;
public bool isM156 = true;
public override bool Equals(object obj)
{
if (!(obj is DTOM156InputModel item))
return false;
if (Process != item.Process)
return false;
if (Type != item.Type)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}
@@ -24,6 +24,9 @@ namespace Thermo.Active.Model.DTOModels.ThModules
public bool Running { get; set; } = false;
public bool HasError { get; set; } = false;
public bool Terminated { get; set; } = false;
public string Category { get; set; } = "";
public string SubCategory_1 { get; set; } = "";
public string SubCategory_2 { get; set; } = "";
public override bool Equals(object obj)
@@ -67,6 +70,12 @@ namespace Thermo.Active.Model.DTOModels.ThModules
return false;
if (Terminated != item.Terminated)
return false;
if (Category != item.Category)
return false;
if (SubCategory_1 != item.SubCategory_1)
return false;
if (SubCategory_2 != item.SubCategory_2)
return false;
return true;
}
@@ -12,6 +12,10 @@ namespace Thermo.Active.Model.DTOModels.ThRecipe
/// </summary>
public string RecipeName = "current.json";
/// <summary>
/// Current DIR of loaded recipe
/// </summary>
public string RecipeDir = "";
/// <summary>
/// User that made last save
/// </summary>
public string UserSave = "";
@@ -35,7 +39,9 @@ namespace Thermo.Active.Model.DTOModels.ThRecipe
/// Note ricetta
/// </summary>
public string recipeNotes = "";
/// <summary>
/// Verifica se sia cambiata da versione letta
/// </summary>
public bool hasChanged = false;
}
}
@@ -63,6 +63,7 @@
<Compile Include="ConfigModels\AlarmsConfigModel.cs" />
<Compile Include="ConfigModels\CmsConnectConfigModel.cs" />
<Compile Include="ConfigModels\ExtSoftwareModel.cs" />
<Compile Include="ConfigModels\InputOperatorConfigModel.cs" />
<Compile Include="ConfigModels\ThermoProdConfigModel.cs" />
<Compile Include="ConfigModels\ModBlockConfigModel.cs" />
<Compile Include="ConfigModels\RiskConfigModel.cs" />
@@ -106,6 +107,7 @@
<Compile Include="DTOModels\DTOAxesModel.cs" />
<Compile Include="DTOModels\DTOAxisNameModel.cs" />
<Compile Include="DTOModels\DTOClientConfigurationModel.cs" />
<Compile Include="DTOModels\DTOM156InputModel.cs" />
<Compile Include="DTOModels\ThModules\DTOModulesBlock.cs" />
<Compile Include="DTOModels\ThProd\DTOProdInfo.cs" />
<Compile Include="DTOModels\ThProd\DTOThermoPanelProd.cs" />
+110 -4
View File
@@ -1270,6 +1270,45 @@ namespace Thermo.Active.NC
return numericalControl.PLC_RM154Data(ref data, ref MTCStatus);
}
public CmsError GetM156Data(out List<DTOM156InputModel> data)
{
data = new List<DTOM156InputModel>();
List<M156InputIsNeededModel> ncData = new List<M156InputIsNeededModel>();
CmsError cmsError = numericalControl.PLC_RM156Data(ref ncData);
if (cmsError.IsError())
return cmsError;
if (ncData.Count() > 0)
{
foreach (var m156Message in ncData)
{
var inp = InputsOperatorConfig.Where(x => m156Message.Id == x.Id).FirstOrDefault();
var buttons = inp.Buttons?.ToDictionary(x => x.Key, x => x.Key.ToString());
if (inp != null)
{
data.Add(new DTOM156InputModel()
{
Id = inp.Id,
isM156 = true,
Buttons = buttons,
Type = inp.Type,
Process = m156Message.Process,
Value = m156Message.Value
});
}
}
}
return NO_ERROR;
}
public CmsError WriteM156Data(int process, double responseValue)
{
return numericalControl.PLC_WM156Response(process, responseValue);
}
public CmsError WriteM154Ack(int processId)
{
return numericalControl.PLC_W154ManageAck(processId);
@@ -1425,7 +1464,16 @@ namespace Thermo.Active.NC
else
{
// confronto ultimo pezzo --> se diverso sono cambiati
dataChanged = (LastProdPanelData.NumDone != lastProdInfoData.NumDone);
bool changeSetpoint = true;
if (lastRecipe != null)
{
if (lastRecipe.ContainsKey("pyrometer_pyrometer_setpoint"))
{
changeSetpoint = LastProdPanelData.TempSetpoint != lastRecipe["pyrometer_pyrometer_setpoint"].SetpointPLC;
}
}
bool changeNumPz = LastProdPanelData.NumDone != lastProdInfoData.NumDone;
dataChanged = changeNumPz || changeSetpoint;
}
}
if (dataChanged)
@@ -1440,10 +1488,24 @@ namespace Thermo.Active.NC
{
currentProdPanel.LastCadenza = Math.Round((double)3600000 / lastProdInfoData.TimeCycleGross, 2);
}
// se il valore SALVATO è > 3 * valore rilevato --> uso SOLO ultimo
if (lastCycle > 3 * currentProdPanel.LastCadenza)
{
lastCycle = currentProdPanel.LastCadenza;
}
// altrimenti EWMA da parametro calcolato standard, default 50%
else
{
//lastCycle = 0.5 * lastCycle + 0.5 * lastDuration;
lastCycle = ewmaLambda * currentProdPanel.LastCadenza + (1 - ewmaLambda) * lastCycle;
}
// salvo anche nei live data della ricetta...
RecipeLiveData.TC_last = lastCycle;
// stima durata da pz fatti...
//currentProdPanel.StimaDurata = Math.Round((currentProdPanel.NumTarget - currentProdPanel.NumDone) * currentProdPanel.LastTCiclo, 2);
// 2020.09.03 uso dati da ricetta corrente... stimata con EWMA
currentProdPanel.StimaDurata = Math.Round((currentProdPanel.NumTarget - currentProdPanel.NumDone) * RecipeLiveData.TC_last, 2);
// 2020.09.03 uso dati da ricetta corrente... stimata con EWMA, in SECONDI!
currentProdPanel.StimaDurata = Math.Round((currentProdPanel.NumTarget - currentProdPanel.NumDone) * lastCycle, 2) ;
// se stima negativa (+ pezzi di quanti richiesti...) --> ZERO!
currentProdPanel.StimaDurata = currentProdPanel.StimaDurata < 0 ? 0 : currentProdPanel.StimaDurata;
}
@@ -1498,6 +1560,7 @@ namespace Thermo.Active.NC
try
{
currentProdPanel.InizioProd = prodInfoFirst.FirstOrDefault().DtEvent;
lottoStart = currentProdPanel.InizioProd;
}
catch
{ }
@@ -2320,6 +2383,46 @@ namespace Thermo.Active.NC
return libraryError;
}
/// <summary>
/// Comparazione elenco parametri della ricetta
/// </summary>
/// <param name="dictSource"></param>
/// <param name="dictDest"></param>
/// <param name="isEqual"></param>
/// <returns></returns>
public CmsError paramsComparer(Dictionary<string, double> dictSource, Dictionary<string, double> dictDest, out bool isEqual)
{
CmsError libraryError = NO_ERROR;
// inizio dal count
isEqual = dictSource.Count==dictDest.Count;
// se uguale conteggio procedo...
if(isEqual)
{
foreach (var pair in dictSource)
{
double value;
if (dictDest.TryGetValue(pair.Key, out value))
{
// Require value be equal.
if (value != pair.Value)
{
isEqual = false;
break;
}
}
else
{
// Require key be present.
isEqual = false;
break;
}
}
}
// return ok!
return libraryError;
}
/// <summary>
/// Legge tutti i parametri della ricetta e calcolo la overview dei vari steps
/// </summary>
@@ -2489,7 +2592,10 @@ namespace Thermo.Active.NC
Visible = currModule.Visible,
Running = currModule.Running,
HasError = currModule.HasError,
Terminated = currModule.Terminated
Terminated = currModule.Terminated,
Category = item.Category,
SubCategory_1 = item.SubCategory_1,
SubCategory_2 = item.SubCategory_2
};
currModules.Add(item.Id, currVal);
}
+51 -38
View File
@@ -611,7 +611,9 @@ namespace Thermo.Active.NC
{
try
{
ReadLiveData();
RecipeLiveData = readLiveData();
// salva current
SaveRecipeCurrent();
}
catch (XmlException ex)
{
@@ -632,9 +634,9 @@ namespace Thermo.Active.NC
/// <summary>
/// Try to load live data from json persistence file
/// </summary>
public static bool ReadLiveData()
public static LiveData readLiveData()
{
bool answ = false;
LiveData answ = null;
if (File.Exists(LIVE_RECIPE_PATH))
{
// load all text data
@@ -642,11 +644,10 @@ namespace Thermo.Active.NC
try
{
// deserialize to object
RecipeLiveData = JsonConvert.DeserializeObject<LiveData>(rawData);
answ = JsonConvert.DeserializeObject<LiveData>(rawData);
}
catch
{ }
answ = true;
}
else
{
@@ -655,18 +656,15 @@ namespace Thermo.Active.NC
try
{
// deserialize to object
RecipeLiveData = JsonConvert.DeserializeObject<LiveData>(rawData);
answ = JsonConvert.DeserializeObject<LiveData>(rawData);
// from template --> reset (if present) overview data...
foreach (var item in RecipeLiveData.RecipeOverview)
{
RecipeLiveData.RecipeOverview[item.Key] = RecipeCatStatus.Unchanged;
answ.RecipeOverview[item.Key] = RecipeCatStatus.Unchanged;
}
}
catch
{ }
// salva current
SaveRecipeCurrent();
answ = true;
}
// rendo se fatto
return answ;
@@ -674,9 +672,9 @@ namespace Thermo.Active.NC
/// <summary>
/// Try to load selected recipe to live data (memory and json persistence file)
/// </summary>
public static bool LoadRecipe(string filePath)
public static LiveData LoadRecipe(string filePath)
{
bool answ = false;
LiveData answ = null;
// check file extension: accetta json / rcp / tpl
if (!(filePath.EndsWith(".json") || filePath.EndsWith(".rcp") || filePath.EndsWith(".tpl")))
{
@@ -689,27 +687,27 @@ namespace Thermo.Active.NC
if (!Path.IsPathRooted(filePath))
{
// controllo se ha path della recipe directory
if (!filePath.Contains(RECIPE_DIRECTORY) && filePath != RECIPE_TEMPLATE_PATH)
if (!filePath.Contains(NcConfig.SharedPath) && filePath != RECIPE_TEMPLATE_PATH)
{
// aggiungo base path!
filePath = RECIPE_DIRECTORY + filePath;
filePath = NcConfig.SharedPath + filePath;
}
}
if (File.Exists(filePath))
{
answ = true;
// load all text data
var rawData = File.ReadAllText(filePath);
try
{
// deserialize to object
RecipeLiveData = JsonConvert.DeserializeObject<LiveData>(rawData);
answ = JsonConvert.DeserializeObject<LiveData>(rawData);
// fix directory se mancasse
answ.RecipeDir = $"{Path.GetDirectoryName(filePath)}\\";
// fix Modifica dati
answ.hasChanged = false;
}
catch
{ }
// update current live data!
SaveRecipeCurrent();
}
// rendo se fatto
return answ;
@@ -731,6 +729,10 @@ namespace Thermo.Active.NC
{
// deserialize to object
RecipeLiveData = JsonConvert.DeserializeObject<LiveData>(rawData);
// fix directory default
RecipeLiveData.RecipeDir = NcConfig.SharedPath;
// fix Modifica dati
RecipeLiveData.hasChanged = false;
// from template --> reset (if present) overview data...
if (RecipeLiveData.RecipeOverview != null)
{
@@ -758,16 +760,18 @@ namespace Thermo.Active.NC
bool answ = false;
try
{
answ = true;
// indico che ora è OK x salvataggio...
RecipeLiveData.hasChanged = false;
// serialize
string rawData = JsonConvert.SerializeObject(RecipeLiveData, Newtonsoft.Json.Formatting.Indented);
// save live!
var dir = Path.GetDirectoryName(LIVE_RECIPE_PATH);
var dir = $"{Path.GetDirectoryName(LIVE_RECIPE_PATH)}\\";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
File.WriteAllText(LIVE_RECIPE_PATH, rawData);
answ = true;
}
catch
{ }
@@ -786,31 +790,38 @@ namespace Thermo.Active.NC
try
{
// save live!
var dir = Path.GetDirectoryName(RECIPE_DIRECTORY);
var dir = $"{Path.GetDirectoryName(NcConfig.SharedPath)}\\";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
// se non ho name valido --> FIX come svg...
if (!filePath.EndsWith(".svg"))
{
filePath = RecipeLiveData.RecipeName.Replace(".rcp", ".svg");
}
// Delete previous image
if (File.Exists(filePath))
{
File.Delete(filePath);
}
if (!Path.IsPathRooted(filePath))
{
// aggiungo base path!
filePath = RECIPE_DIRECTORY + filePath;
// aggiungo base path! se ho path della ricetta corrente uso quella...
string currPath = RecipeLiveData.RecipeDir;
if (!string.IsNullOrEmpty(currPath))
{
currPath = NcConfig.SharedPath;
}
filePath = $"{currPath}{filePath}";
}
// Save NEW image
File.WriteAllBytes(filePath, fileData);
imageUploaded = true;
}
catch
{ }
return imageUploaded;
}
/// <summary>
@@ -820,6 +831,7 @@ namespace Thermo.Active.NC
{
// duplicate data...
LiveData data2save = RecipeLiveData;
data2save.RecipeDir = NcConfig.SharedPath;
// template --> reset overview data...
data2save.RecipeOverview = new Dictionary<RecipeSection, RecipeCatStatus>();
return SaveRecipe(RECIPE_TEMPLATE_PATH, data2save);
@@ -839,24 +851,25 @@ namespace Thermo.Active.NC
// default: ricetta!
filePath += ".rcp";
}
string fileName = Path.GetFileName(filePath);
// fix name!
currRecipe.RecipeName = fileName;
// serialize
string rawData = JsonConvert.SerializeObject(currRecipe, Newtonsoft.Json.Formatting.Indented);
// save live!
File.WriteAllText(LIVE_RECIPE_PATH, rawData);
// verifica path
if (!Path.IsPathRooted(filePath))
{
// controllo se ha path della recipe directory
if (!filePath.Contains(RECIPE_DIRECTORY) && filePath != RECIPE_TEMPLATE_PATH)
if (!filePath.Contains(NcConfig.SharedPath) && filePath != RECIPE_TEMPLATE_PATH)
{
// aggiungo base path!
filePath = RECIPE_DIRECTORY + filePath;
filePath = NcConfig.SharedPath + filePath;
}
}
// fix name, path, modifica!
currRecipe.RecipeName = Path.GetFileName(filePath);
currRecipe.RecipeDir = $"{Path.GetDirectoryName(filePath)}\\";
currRecipe.hasChanged = false;
// serialize
string rawData = JsonConvert.SerializeObject(currRecipe, Newtonsoft.Json.Formatting.Indented);
// save live!
File.WriteAllText(LIVE_RECIPE_PATH, rawData);
// save!
File.WriteAllText(filePath, rawData);
}
@@ -183,6 +183,19 @@ namespace Thermo.Active.Controllers.SignalR
}
#endif
[SignalRAuthorize(FunctionAccess = GENERAL, Action = ACTIONS.WRITE)]
public void WriteM156Response(int process, double responseVal)
{
using (NcAdapter ncAdapter = new NcAdapter())
{
ncAdapter.Connect();
CmsError cmsError = ncAdapter.WriteM156Data(process, responseVal);
if (cmsError.IsError())
throw new HubException(cmsError.localizationKey);
}
}
[SignalRAuthorize(FunctionAccess = GENERAL, Action = ACTIONS.WRITE)]
public void WriteScadaValue(string memIndex, SCADA_MEM_TYPE memType, object value)
{
@@ -55,10 +55,10 @@ namespace Thermo.Active.Controllers.WebApi
Dictionary<string, string> alarmsNames = GetPlcAlarmsTranslations(language);
Dictionary<string, string> headsNames = GetLocalizedHeadsNames(language);
// scada
Dictionary<string, string> scadaTranslations = GetScadaTranslations(language);
Dictionary<string, string> m156Translations = GetM156Translations(language);
// THermo (modules and recipe enums) translations
// Thermo (modules and recipe enums) translations
Dictionary<string, string> thermoTranslations = GetThermoTranslations(language);
@@ -72,7 +72,9 @@ namespace Thermo.Active.Controllers.WebApi
translations = translations.Concat(headsNames).ToDictionary(x => x.Key, x => x.Value);
// Scada
translations = translations.Concat(scadaTranslations).ToDictionary(x => x.Key, x => x.Value);
// Scada
// M156
translations = translations.Concat(m156Translations).ToDictionary(x => x.Key, x => x.Value);
// Thermo
translations = translations.Concat(thermoTranslations).ToDictionary(x => x.Key, x => x.Value);
if (translations == null)
@@ -210,5 +212,31 @@ namespace Thermo.Active.Controllers.WebApi
return translatedNames;
}
public static Dictionary<string, string> GetM156Translations(string language)
{
Dictionary<string, string> translatedNames = new Dictionary<string, string>();
foreach (var input in InputsOperatorConfig)
{
translatedNames.Add(
"m156_title_" + input.Id,
GetValueFromLocalizationList(input.Messages, language, "Title_" + input.Id)
);
if (input.Buttons != null && input.Buttons.Count() > 0)
foreach (var button in input.Buttons)
{
// Add translated button text to the list
// Buttons have a Dictionary<string, string> with the translations
translatedNames.Add(
"m156_" + input.Id + "_button_" + button.Key,
GetValueFromLocalizationList(button.Value, language, "Button_" + button.Key)
);
}
}
return translatedNames;
}
}
}
@@ -70,6 +70,16 @@ namespace Thermo.Active.Controllers.WebApi
return Ok(currRecipe);
}
[Route("recipeNotes"), HttpGet]
public IHttpActionResult GetRecipeNotes()
{
return Ok(NcAdapter.RecipeLiveData.recipeNotes);
}
[Route("recipeChanged"), HttpGet]
public IHttpActionResult GetChanged()
{
return Ok(NcAdapter.RecipeLiveData.hasChanged);
}
[Route("update"), HttpPut]
[WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.RECIPE_MANAGER, Action = ACTIONS.READ)]
@@ -77,7 +87,7 @@ namespace Thermo.Active.Controllers.WebApi
{
if (parametersList != null)
{
if (NcFileAdapter.RecipeLiveData != null)
if (NcAdapter.RecipeLiveData != null)
{
// Try connection
CmsError libraryError = ncAdapter.Connect();
@@ -119,6 +129,8 @@ namespace Thermo.Active.Controllers.WebApi
if (updtRecipe.Count > 0)
{
// salvo che la ricetta è cambiata
NcAdapter.RecipeLiveData.hasChanged = true;
// scrivo sul PLC con i parametri specificati x ritardo/raggruppamento
ncAdapter.WriteRecipeParams(updtRecipe, ncAdapter.nMaxParamWrite, ncAdapter.delayParamWrite);
}
@@ -148,7 +160,7 @@ namespace Thermo.Active.Controllers.WebApi
[WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.RECIPE_MANAGER, Action = ACTIONS.READ)]
public IHttpActionResult ConfirmEdit(RecipeSection section)
{
if (NcFileAdapter.RecipeLiveData != null)
if (NcAdapter.RecipeLiveData != null)
{
// Try connection
CmsError libraryError = ncAdapter.Connect();
@@ -211,7 +223,7 @@ namespace Thermo.Active.Controllers.WebApi
[WebApiAuthorize(FunctionAccess = FUNCTIONALITY_NAMES.RECIPE_MANAGER, Action = ACTIONS.READ)]
public IHttpActionResult CancelEdit()
{
if (NcFileAdapter.RecipeLiveData != null)
if (NcAdapter.RecipeLiveData != null)
{
// Try connection
CmsError libraryError = ncAdapter.Connect();
@@ -231,6 +243,7 @@ namespace Thermo.Active.Controllers.WebApi
// recupero i dati LIVE dei parametri HMI della ricetta...
libraryError = ncAdapter.ReadFullRecipe(out Dictionary<string, DTORecipeParam> currRecipe);
if (libraryError.IsError())
{
ThermoActiveLogger.LogError($"ConfirmEdit error | {libraryError.exception}");
@@ -267,13 +280,18 @@ namespace Thermo.Active.Controllers.WebApi
{
// chiamo metodo di lettura...
bool fatto = NcFileAdapter.LoadRecipe(newName);
if (!fatto)
var dataRead = NcFileAdapter.LoadRecipe(newName);
if (dataRead == null)
{
ThermoActiveLogger.LogError($"LoadRecipe error");
return NotFound();
}
// salvo in memoria ricetta live
NcAdapter.RecipeLiveData = dataRead;
// update current live data!
NcFileAdapter.SaveRecipeCurrent();
CmsError libraryError = WriteCurrentRecipeToPlc();
if (libraryError.IsError())
{
@@ -330,7 +348,7 @@ namespace Thermo.Active.Controllers.WebApi
using (UsersController usersController = new UsersController())
{
var userData = usersController.GetUserInfo(Convert.ToInt32(userId.Value));
NcFileAdapter.RecipeLiveData.UserSave = userData.Id.ToString();
NcAdapter.RecipeLiveData.UserSave = userData.Id.ToString();
}
// recupero i dati LIVE dei parametri HMI della ricetta...
@@ -368,7 +386,7 @@ namespace Thermo.Active.Controllers.WebApi
using (UsersController usersController = new UsersController())
{
var userData = usersController.GetUserInfo(Convert.ToInt32(userId.Value));
NcFileAdapter.RecipeLiveData.UserSave = userData.Id.ToString();
NcAdapter.RecipeLiveData.UserSave = userData.Id.ToString();
}
// recupero i dati LIVE dei parametri HMI della ricetta...
@@ -386,9 +404,9 @@ namespace Thermo.Active.Controllers.WebApi
}
// ora salvo ANCHE i dati live...
NcFileAdapter.RecipeLiveData.RecipeParameters = currParams;
NcAdapter.RecipeLiveData.RecipeParameters = currParams;
// e salvo su disco
NcFileAdapter.SaveRecipe(newName, NcFileAdapter.RecipeLiveData);
NcFileAdapter.SaveRecipe(newName, NcAdapter.RecipeLiveData);
// ritorno solo fatto!
return Ok();
@@ -426,7 +444,7 @@ namespace Thermo.Active.Controllers.WebApi
}
// ora salvo nei dati live...
NcFileAdapter.RecipeLiveData.RecipeParameters = currParams;
NcAdapter.RecipeLiveData.RecipeParameters = currParams;
// carico i dati dei riscaldi...
var currChSet = new Dictionary<int, int>();
@@ -434,7 +452,7 @@ namespace Thermo.Active.Controllers.WebApi
{
currChSet.Add(item.Key, item.Value.SetpointHMI);
}
NcFileAdapter.RecipeLiveData.ChannelSetpoints = currChSet;
NcAdapter.RecipeLiveData.ChannelSetpoints = currChSet;
// e salvo su disco
@@ -479,10 +497,10 @@ namespace Thermo.Active.Controllers.WebApi
// salvo note...
if (!string.IsNullOrEmpty(recipeNotes))
{
NcFileAdapter.RecipeLiveData.recipeNotes = recipeNotes.Trim();
NcAdapter.RecipeLiveData.recipeNotes = recipeNotes.Trim();
}
// e salvo su disco
NcFileAdapter.SaveRecipe(NcFileAdapter.RecipeLiveData.RecipeName, NcFileAdapter.RecipeLiveData);
NcFileAdapter.SaveRecipe(NcAdapter.RecipeLiveData.RecipeName, NcAdapter.RecipeLiveData);
return Ok();
}
@@ -500,13 +518,13 @@ namespace Thermo.Active.Controllers.WebApi
using (UsersController usersController = new UsersController())
{
var userData = usersController.GetUserInfo(Convert.ToInt32(userId.Value));
NcFileAdapter.RecipeLiveData.UserSave = userData.Id.ToString();
NcAdapter.RecipeLiveData.UserSave = userData.Id.ToString();
}
// salvo note...
if (!string.IsNullOrEmpty(recipeNotes))
{
NcFileAdapter.RecipeLiveData.recipeNotes = recipeNotes.Trim();
NcAdapter.RecipeLiveData.recipeNotes = recipeNotes.Trim();
}
bool imageUploaded = false;
@@ -545,15 +563,17 @@ namespace Thermo.Active.Controllers.WebApi
currParams.Add(item.Key, item.Value.SetpointPLC);
}
// salvo parametri
NcFileAdapter.RecipeLiveData.RecipeParameters = currParams;
NcAdapter.RecipeLiveData.RecipeParameters = currParams;
// ora salvo il dato del TC stimato se presente
if (estimTimeSec > 0)
{
NcFileAdapter.RecipeLiveData.TC_last = estimTimeSec;
NcAdapter.RecipeLiveData.TC_last = estimTimeSec;
}
// indico che non è + modificata...
NcAdapter.RecipeLiveData.hasChanged = false;
// e salvo su disco
NcFileAdapter.SaveRecipe(newName, NcFileAdapter.RecipeLiveData);
NcFileAdapter.SaveRecipe(newName, NcAdapter.RecipeLiveData);
}
return Ok();
}
@@ -567,7 +587,13 @@ namespace Thermo.Active.Controllers.WebApi
try
{
// ora salvo ANCHE i dati live...
NcFileAdapter.RecipeLiveData.RecipeParameters = currParams;
NcAdapter.RecipeLiveData.RecipeParameters = currParams;
// verifico SE siano cambiati rispetto a versione originale...
var originalRecipe = NcFileAdapter.LoadRecipe($"{NcAdapter.RecipeLiveData.RecipeDir}{NcAdapter.RecipeLiveData.RecipeName}");
// comparazione parametri...
bool isEqual = false;
ncAdapter.paramsComparer(originalRecipe.RecipeParameters, currParams, out isEqual);
NcAdapter.RecipeLiveData.hasChanged = !isEqual;
// e salvo su disco
NcFileAdapter.SaveRecipeCurrent();
}
@@ -604,7 +630,7 @@ namespace Thermo.Active.Controllers.WebApi
// save parameters to PLC!!!
Dictionary<string, DTORecipeParam> updtRecipe = new Dictionary<string, DTORecipeParam>();
foreach (var item in NcFileAdapter.RecipeLiveData.RecipeParameters)
foreach (var item in NcAdapter.RecipeLiveData.RecipeParameters)
{
if (prevRecipe.ContainsKey(item.Key))
{
@@ -632,7 +658,7 @@ namespace Thermo.Active.Controllers.WebApi
// process ch load setup...
Dictionary<int, int> newRisk = new Dictionary<int, int>();
foreach (var item in NcFileAdapter.RecipeLiveData.ChannelSetpoints)
foreach (var item in NcAdapter.RecipeLiveData.ChannelSetpoints)
{
newRisk.Add(item.Key, item.Value);
}
@@ -238,7 +238,7 @@ namespace Thermo.Active.Controllers.WebApi
try
{
// ora salvo ANCHE i dati live...
NcFileAdapter.RecipeLiveData.ChannelSetpoints = chSetpoints;
NcAdapter.RecipeLiveData.ChannelSetpoints = chSetpoints;
// e salvo su disco
NcFileAdapter.SaveRecipeCurrent();
}
@@ -86,6 +86,10 @@ namespace Thermo.Active.Listeners
{
SignalRListener.SetGatewayRebootStatus(a);
}));
infos.Add(MessageServices.Current.Subscribe(SEND_M156_DATA, (a, b) =>
{
SignalRListener.SendM156Data(a);
}));
// add specific modules for THERMO
infos.Add(MessageServices.Current.Subscribe(SEND_THERMO_RECIPE_FULL, (a, b) =>
@@ -239,6 +239,17 @@ namespace Thermo.Active.Listeners.SignalR
}
}
#endif
public static void SendM156Data(object data)
{
List<DTOM156InputModel> dtoM156Data = data as List<DTOM156InputModel>;
if (!LastM156Data.SequenceEqual(dtoM156Data))
{
LastM156Data = dtoM156Data;
var context = GlobalHost.ConnectionManager.GetHubContext<NcHub>();
context.Clients.Group("ncData").m156Data(dtoM156Data);
}
}
public static void SendScadaData(object scada)
{
@@ -455,6 +466,8 @@ namespace Thermo.Active.Listeners.SignalR
}
}
public static void SetGatewayRebootStatus(object status)
{
string msg = status.ToString();
@@ -514,7 +527,9 @@ namespace Thermo.Active.Listeners.SignalR
group.magazineIsActive(LastNcMagazineIsActive);
// Send PP Queue
group.partProgramQueue(LastPartProgramQueue);
// Send m156
group.m156Data(LastM156Data);
// Send Scada
group.scadaData(LastScadaData);
@@ -30,6 +30,7 @@ namespace Thermo.Active.Listeners
#if false
public static List<DTOM155InputModel> LastM155Data = new List<DTOM155InputModel>();
#endif
public static List<DTOM156InputModel> LastM156Data = new List<DTOM156InputModel>();
public static List<DTOScadaModel> LastScadaData = new List<DTOScadaModel>();
// FIXME TODO inserire oggetti corretti per THERMO
+1 -1
View File
@@ -30,4 +30,4 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("0.12.69")]
[assembly: AssemblyVersion("0.14.80")]
@@ -2378,7 +2378,7 @@
line-height: 4px;
.title {
width: 110px;
min-width: 110px;
text-align: right;
color: @color-darkish-blue;
}
@@ -256,11 +256,11 @@
align-items: center;
display: flex;
border-radius: 7px;
&.fa-check-circle {
color: #90BF3D;
}
&.fa-check-circle.undone-step {
color: #1791FF;
}
@@ -474,6 +474,8 @@ article {
padding-bottom: 10px;
padding-top: 5px;
border: 2px solid #979797;
box-sizing: border-box;
align-items: center;
}
.submit {
@@ -385,6 +385,8 @@ article .box .body {
padding-bottom: 10px;
padding-top: 5px;
border: 2px solid #979797;
box-sizing: border-box;
align-items: center;
}
.box .submit {
margin: auto;
@@ -2521,7 +2523,7 @@ article .box .body {
.modal.modal-add-element-queue .modal-load-program-body .selected-item .selected-item-header .subtitle .title,
.modal.modal-load-program .modal-add-element-queue-body .selected-item .selected-item-header .subtitle .title,
.modal.modal-add-element-queue .modal-add-element-queue-body .selected-item .selected-item-header .subtitle .title {
width: 110px;
min-width: 110px;
text-align: right;
color: #002680;
}
@@ -7,301 +7,301 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="disegno-quote-velocita-stampo.svg"
id="svg8"
version="1.1"
viewBox="0 0 297 210"
width="297"
height="210"
width="297">
viewBox="0 0 297 210"
version="1.1"
id="svg8"
sodipodi:docname="disegno-quote-velocita-stampo.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)">
<sodipodi:namedview
inkscape:object-nodes="false"
inkscape:current-layer="layer1"
inkscape:window-maximized="1"
inkscape:window-y="-9"
inkscape:window-x="-9"
inkscape:cy="51.482576"
inkscape:cx="187.49592"
inkscape:zoom="2.9247028"
showgrid="false"
id="namedview907"
inkscape:window-height="1001"
inkscape:window-width="1920"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
inkscape:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
pagecolor="#ffffff" />
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1056"
id="namedview907"
showgrid="false"
inkscape:zoom="2.9247028"
inkscape:cx="187.49592"
inkscape:cy="51.482576"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:object-nodes="false" />
<defs
id="defs2">
<inkscape:perspective
id="perspective32764"
inkscape:persp3d-origin="148.5 : 70 : 1"
inkscape:vp_z="297 : 105 : 1"
inkscape:vp_y="0 : 1000 : 0"
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 105 : 1"
sodipodi:type="inkscape:persp3d" />
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="297 : 105 : 1"
inkscape:persp3d-origin="148.5 : 70 : 1"
id="perspective32764" />
<marker
inkscape:isstock="true"
inkscape:stockid="ExperimentalArrow"
orient="auto-start-reverse"
refY="3.0"
refX="5.0"
id="marker1181"
refX="5.0"
refY="3.0"
orient="auto-start-reverse"
inkscape:stockid="ExperimentalArrow">
inkscape:isstock="true">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
id="path1179"
d="m 10,3 -10,3 0,-6 z"
id="path1179" />
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible;"
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lend"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow1Lend">
<path
transform="scale(0.8) rotate(180) translate(12.5,0)"
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path912" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="marker1176"
style="overflow:visible">
<path
id="path1055"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
transform="scale(0.8)" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutL"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.8)"
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path1162" />
</marker>
<marker
orient="auto-start-reverse"
refY="3.0"
refX="5.0"
id="marker4178">
<path
id="path4176"
d="m 10,3 -10,3 0,-6 z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
orient="auto-start-reverse"
refY="3.0"
refX="5.0"
id="marker4066">
<path
id="path4064"
d="m 10,3 -10,3 0,-6 z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
orient="auto-start-reverse"
refY="3.0"
refX="5.0"
id="marker3448">
<path
id="path3446"
d="m 10,3 -10,3 0,-6 z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
id="marker3312"
refX="5.0"
refY="3.0"
orient="auto-start-reverse">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="m 10,3 -10,3 0,-6 z"
id="path3310" />
</marker>
<marker
style="overflow:visible"
id="StopL"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.8)"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1"
d="M 0.0,5.65 L 0.0,-5.65"
id="path1189" />
</marker>
<marker
orient="auto-start-reverse"
refY="3.0"
refX="5.0"
id="marker1984">
<path
id="path1982"
d="m 10,3 -10,3 0,-6 z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
orient="auto-start-reverse"
refY="3.0"
refX="5.0"
id="marker1920">
<path
id="path1918"
d="m 10,3 -10,3 0,-6 z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
style="overflow:visible"
id="marker1502"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.6) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path1500" />
</marker>
<marker
style="overflow:visible"
id="Arrow2Sstart"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.3) translate(-2.3,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#bbbcbc;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
id="path1050" />
</marker>
<marker
style="overflow:visible"
id="marker1406"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.4) translate(10,0)"
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path1404" />
</marker>
<marker
style="overflow:visible;"
id="Arrow2Mend"
refX="0.0"
refY="0.0"
orient="auto">
inkscape:isstock="true">
<path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#bbbcbc;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
id="path1047" />
</marker>
<marker
style="overflow:visible"
id="Arrow2Mstart"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.6) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#bbbcbc;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
id="path1044" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Mstart"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.4) translate(10,0)"
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
id="path912"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path1026" />
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
transform="scale(0.8) rotate(180) translate(12.5,0)" />
</marker>
<marker
id="ExperimentalArrow"
style="overflow:visible"
id="marker1176"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.8)"
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path1055" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutL"
style="overflow:visible">
<path
id="path1162"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
transform="scale(0.8)" />
</marker>
<marker
id="marker4178"
refX="5.0"
refY="3.0"
orient="auto-start-reverse">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="m 10,3 -10,3 0,-6 z"
id="path1273" />
id="path4176" />
</marker>
<marker
id="marker4066"
refX="5.0"
refY="3.0"
orient="auto-start-reverse">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="m 10,3 -10,3 0,-6 z"
id="path4064" />
</marker>
<marker
id="marker3448"
refX="5.0"
refY="3.0"
orient="auto-start-reverse">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="m 10,3 -10,3 0,-6 z"
id="path3446" />
</marker>
<marker
orient="auto-start-reverse"
refY="3"
refX="5"
id="ExperimentalArrow-5">
refY="3.0"
refX="5.0"
id="marker3312">
<path
id="path1273-2"
d="M 10,3 0,6 V 0 Z"
id="path3310"
d="m 10,3 -10,3 0,-6 z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="StopL"
style="overflow:visible">
<path
id="path1189"
d="M 0.0,5.65 L 0.0,-5.65"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1"
transform="scale(0.8)" />
</marker>
<marker
id="marker1984"
refX="5.0"
refY="3.0"
orient="auto-start-reverse">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="m 10,3 -10,3 0,-6 z"
id="path1982" />
</marker>
<marker
id="marker1920"
refX="5.0"
refY="3.0"
orient="auto-start-reverse">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="m 10,3 -10,3 0,-6 z"
id="path1918" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="marker1502"
style="overflow:visible">
<path
id="path1500"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) translate(0,0)" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Sstart"
style="overflow:visible">
<path
id="path1050"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#bbbcbc;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.3) translate(-2.3,0)" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="marker1406"
style="overflow:visible">
<path
id="path1404"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
transform="scale(0.4) translate(10,0)" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path1047"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#bbbcbc;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mstart"
style="overflow:visible">
<path
id="path1044"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#bbbcbc;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) translate(0,0)" />
</marker>
<marker
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mstart"
style="overflow:visible">
<path
id="path1026"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1;fill:#bbbcbc;fill-opacity:1"
transform="scale(0.4) translate(10,0)" />
</marker>
<marker
orient="auto-start-reverse"
refY="3"
refX="5"
id="ExperimentalArrow-2">
refY="3.0"
refX="5.0"
id="ExperimentalArrow">
<path
id="path1273-1"
d="M 10,3 0,6 V 0 Z"
id="path1273"
d="m 10,3 -10,3 0,-6 z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
orient="auto-start-reverse"
refY="3"
refX="5"
id="ExperimentalArrow-3">
<path
id="path1273-6"
d="M 10,3 0,6 V 0 Z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1" />
</marker>
<marker
id="marker1984-1"
id="ExperimentalArrow-5"
refX="5"
refY="3"
orient="auto-start-reverse">
<path
inkscape:connector-curvature="0"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="M 10,3 0,6 V 0 Z"
id="path1982-1" />
id="path1273-2" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="StopL-8"
style="overflow:visible">
id="ExperimentalArrow-2"
refX="5"
refY="3"
orient="auto-start-reverse">
<path
inkscape:connector-curvature="0"
id="path1189-1"
d="M 0,5.65 V -5.65"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="M 10,3 0,6 V 0 Z"
id="path1273-1" />
</marker>
<marker
id="ExperimentalArrow-3"
refX="5"
refY="3"
orient="auto-start-reverse">
<path
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
d="M 10,3 0,6 V 0 Z"
id="path1273-6" />
</marker>
<marker
orient="auto-start-reverse"
refY="3"
refX="5"
id="marker1984-1">
<path
id="path1982-1"
d="M 10,3 0,6 V 0 Z"
style="fill:context-stroke;stroke:#bbbcbc;stroke-opacity:1"
inkscape:connector-curvature="0" />
</marker>
<marker
style="overflow:visible"
id="StopL-8"
refX="0"
refY="0"
orient="auto">
<path
transform="scale(0.8)"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#bbbcbc;stroke-width:1pt;stroke-opacity:1"
transform="scale(0.8)" />
d="M 0,5.65 V -5.65"
id="path1189-1"
inkscape:connector-curvature="0" />
</marker>
</defs>
<metadata
@@ -319,227 +319,207 @@
<g
id="layer1">
<path
inkscape:connector-curvature="0"
id="path963"
style="fill:#979797;fill-opacity:1;stroke:#979797;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 156.931,209.46621 156.11454,0.72523508"
style="fill:#979797;fill-opacity:1;stroke:#979797;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path963"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path967"
d="m 26.520225,102.56968 101.609595,0.0638 v 0 0 0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 27.881686,115.27013 26.766659,0.0328 v 0 0 0"
id="path967-2" />
d="m 26.520225,170.56968 101.609595,0.0638 v 0 0 0"
id="path967"
inkscape:connector-curvature="0" />
<path
id="path967-2"
d="m 27.881686,183.27013 26.766659,0.0328 v 0 0 0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<g
style="fill:#99cfff;fill-rule:evenodd;stroke:#002680;stroke-width:4;stroke-linejoin:round"
transform="matrix(0.26458333,0,0,0.26458333,36.848572,131.73977)"
id="g842"
transform="matrix(0.26458333,0,0,0.26458333,36.848572,63.739768)">
style="fill:#99cfff;fill-rule:evenodd;stroke:#002680;stroke-width:4;stroke-linejoin:round">
<rect
id="Rectangle-Copy"
x="11"
y="147"
height="48"
width="334"
height="48" />
y="147"
x="11"
id="Rectangle-Copy" />
<rect
id="Rectangle-Copy-2"
x="0"
y="199"
height="4"
width="356"
height="4" />
y="199"
x="0"
id="Rectangle-Copy-2" />
<polygon
id="Rectangle"
points="68,147 98,0 257.62107,0 287.62107,147 " />
points="257.62107,0 287.62107,147 68,147 98,0 "
id="Rectangle" />
<polygon
id="polygon840"
points="257.62107,147 287.62107,179 68,179 98,147 "
transform="matrix(1,0,0,-1,0,326)"
points="68,179 98,147 257.62107,147 287.62107,179 " />
id="polygon840" />
</g>
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.506214;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 128.13064,169.64777 -0.94836,-79.838053 v 0"
id="path1004"
d="m 128.13064,101.64777 -0.94836,-79.838053 v 0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.506214;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.507232;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 7.7467941,90.012593 144.96042,89.468186 v 0 0"
id="path1006"
d="M 7.7467941,22.012593 144.96042,21.468186 v 0 0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.507232;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 81.767182,90.017085 V 100.08673"
id="path1012"
d="M 81.767182,22.017085 V 32.086727"
style="fill:none;stroke:#bbbcbc;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path1014"
d="m 31.419476,115.38155 -5.26e-4,69.84397 v 0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.510365;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1016"
d="M 14.545513,185.5762 141.91289,185.30405"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1018"
style="fill:none;stroke:#bbbcbc;stroke-width:0.530934;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 169.07902,100.69655 32.94825,0.0636 v 0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.530934;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path1018"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.420779;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:"
d="M 136.74199,21.332161 V 32.475182"
id="path1012-1" />
id="path1012-1"
d="M 136.74199,89.332161 V 100.47518"
style="fill:none;stroke:#bbbcbc;stroke-width:0.420779;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.573405;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#StopL)"
d="m 8.1373094,90.089263 v 7.19921"
id="path1012-1-6"
d="m 8.1373094,22.089263 v 7.19921"
style="fill:none;stroke:#bbbcbc;stroke-width:0.573405;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#StopL)" />
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path3838"
style="fill:none;stroke:#bbbcbd;stroke-width:0.525286;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 209.62896,109.94961 h -40.55073 v 0"
style="fill:none;stroke:#bbbcbd;stroke-width:0.525286;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path3838"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4058"
style="fill:none;stroke:#bbbcbc;stroke-width:0.517246;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 274.94556,100.96858 h 16.60132"
style="fill:none;stroke:#bbbcbc;stroke-width:0.517246;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path4058"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4060"
style="fill:none;stroke:#bbbcbc;stroke-width:0.505291;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 278.21139,114.84836 H 291.2747"
style="fill:none;stroke:#bbbcbc;stroke-width:0.505291;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path4060"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1012-9"
d="m 290.73041,100.87214 v 14.2521"
id="path1012-9" />
style="fill:none;stroke:#bbbcbc;stroke-width:0.5132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<g
style="fill:#99cfff;fill-rule:evenodd;stroke:#002680;stroke-width:4;stroke-linejoin:round"
transform="matrix(0.26458333,0,0,0.26458333,184.30533,62.410816)"
id="g918"
transform="matrix(0.26458333,0,0,0.26458333,184.30533,62.410816)">
style="fill:#99cfff;fill-rule:evenodd;stroke:#002680;stroke-width:4;stroke-linejoin:round">
<rect
id="Rectangle-Copy-5"
x="11"
y="147"
height="48"
width="334"
height="48" />
y="147"
x="11"
id="Rectangle-Copy-5" />
<rect
id="Rectangle-Copy-2-3"
x="0"
y="199"
height="4"
width="356"
height="4" />
y="199"
x="0"
id="Rectangle-Copy-2-3" />
<polygon
id="Rectangle-1"
points="257.62107,0 287.62107,147 68,147 98,0 " />
points="257.62107,0 287.62107,147 68,147 98,0 "
id="Rectangle-1" />
<polygon
id="polygon916"
points="257.62107,147 287.62107,179 68,179 98,147 "
transform="matrix(1,0,0,-1,0,326)"
points="257.62107,147 287.62107,179 68,179 98,147 " />
id="polygon916" />
</g>
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.544692;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1012-5"
d="M 178.87573,62.402301 V 100.34339"
id="path1012-5" />
style="fill:none;stroke:#bbbcbc;stroke-width:0.544692;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path6954"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 209.90111,62.050773 H 178.05927"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path6954"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path7676"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 290.73042,115.10538 v 4.08228 0"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path7676"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path7678"
style="fill:none;stroke:#bbbcbc;stroke-width:0.585901;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:"
d="M 172.07191,96.971722 V 109.67746"
style="fill:none;stroke:#bbbcbc;stroke-width:0.585901;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:" />
id="path7678"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path7680"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 172.07191,110.04689 v 8.33945"
style="fill:none;stroke:#bbbcbc;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path7680"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path1012-1-6-1"
d="M 28.871798,89.809207 V 170.21125"
style="fill:none;stroke:#bbbcbc;stroke-width:0.510445;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 28.871798,21.809207 V 102.21125"
id="path1012-1-6-1" />
inkscape:connector-curvature="0" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 178.74528,61.891705 -1.99023,4.161391 h 3.98046 z"
id="path32784"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path32784"
d="m 178.74528,61.891705 -1.99023,4.161391 h 3.98046 z"
id="path32784-2"
d="m 81.78039,89.621142 -1.99023,4.16139 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 81.78039,21.621142 -1.99023,4.16139 h 3.98046 z"
id="path32784-2"
inkscape:connector-curvature="0" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 28.948815,21.983004 -1.99023,4.16139 h 3.98046 z"
inkscape:connector-curvature="0"
id="path32784-6"
inkscape:connector-curvature="0" />
d="m 28.948815,89.983004 -1.99023,4.16139 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 8.1418572,21.802077 -1.99023,4.16139 h 3.9804598 z"
inkscape:connector-curvature="0"
id="path32784-5"
inkscape:connector-curvature="0" />
d="m 8.1418572,89.802077 -1.99023,4.16139 h 3.9804598 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 31.481832,115.52385 -1.99023,4.16139 h 3.98046 z"
id="path32784-57"
inkscape:connector-curvature="0" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 290.75461,101.04944 -1.99023,4.16139 h 3.98046 z"
inkscape:connector-curvature="0"
id="path32784-51"
inkscape:connector-curvature="0" />
d="m 290.75461,101.04944 -1.99023,4.16139 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 172.0645,109.91501 -1.99023,4.16139 h 3.98046 z"
inkscape:connector-curvature="0"
id="path32784-23"
inkscape:connector-curvature="0" />
d="m 172.0645,109.91501 -1.99023,4.16139 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.299082px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278896px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 81.961325,35.733683 -1.99023,-3.6186 h 3.98046 z"
inkscape:connector-curvature="0"
id="path32784-3"
d="m 81.961325,103.73368 -1.99023,-3.6186 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278896px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 136.96407,103.64323 -1.99023,-3.6186 h 3.98046 z"
id="path32784-3-7"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path32784-3-7"
d="m 136.96407,35.643226 -1.99023,-3.6186 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path32784-3-73"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 172.24542,100.41618 -1.99023,-3.618595 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
id="path32784-3-73"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path32784-3-2"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 178.93983,100.41619 -1.99023,-3.6186 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
id="path32784-3-2"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path32784-3-70"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 290.75462,114.89059 -1.99023,-3.6186 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
id="path32784-3-70"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 127.93118,170.04456 -1.99023,-3.6186 h 3.98046 z"
id="path32784-3-5"
d="m 127.93118,102.04456 -1.99023,-3.618604 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path32784-3-24"
d="m 31.481836,185.27239 -1.99023,-3.6186 h 3.98046 z"
style="fill:#bbbcbc;fill-opacity:1;stroke:none;stroke-width:0.278897px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 20 KiB

@@ -4,5 +4,5 @@
"enabled": true,
"apiServerUrl": "http://localhost:9000/"
},
"allUIVisible": true
"allUIVisible": false
}
+1
View File
@@ -20,6 +20,7 @@
<body>
<app>
<img src="/assets/icons/png/bg.png" style="display:hidden">
<div id="loading-spinner"><i class="fa fa-circle-o-notch fa-spin"></i></div>
</app>
<script src="/dist/vendors~main.js" type="text/javascript"></script>
+2
View File
@@ -18,5 +18,7 @@ declare module server {
terminated: boolean;
running: boolean;
hasError: boolean;
category: string;
subCategory_1: string;
}
}
+8
View File
@@ -20,6 +20,7 @@ import Component from "vue-class-component";
import { Watch } from "vue-property-decorator";
import { UsersService } from "./services/usersService";
import { KeyboardHelper } from "./app_modules_thermo/components/KeyboardHelper";
import printGantt from "@/app_modules_thermo/processo/components/printProcesso.vue";
declare var cmsClient;
@@ -37,6 +38,7 @@ declare var cmsClient;
dashboard: Dashboard,
predashboard: PreDashboard,
alarmList,
printGantt
}
})
export default class app extends Vue {
@@ -64,6 +66,12 @@ export default class app extends Vue {
mounted() {
let ms = messageService;
window.oncontextmenu = function (event) {
event.preventDefault();
event.stopPropagation();
return false;
};
// if cms is connected
if (typeof cmsClient != "undefined")
this.HMIsrc = cmsClient.getScreenBase64();
+1
View File
@@ -18,6 +18,7 @@
<paddle></paddle>
<app-footer :class="{'blur':(applyBlur || applyBlurNc)}"></app-footer>
<print-gantt></print-gantt>
</div>
<modal-container containerName="modal-login" name="modal-login"></modal-container>
<keyboard></keyboard>
@@ -23,7 +23,7 @@
justify-content: center;
height: 100%;
font-family: "Work Sans";
font-size: 47px;
font-size: 40px;
font-weight: 500;
line-height: 1.01;
padding-right: 1rem;
@@ -11,7 +11,7 @@ export default class Combo extends Vue {
get options() {
let result: { id: number, text: string, anim: string }[] = [];
if (this.value.enumVal)
if (this.value && this.value.enumVal)
for (const key in this.value.enumVal) {
if (this.value.enumVal.hasOwnProperty(key)) {
const element = this.value.enumVal[key];
@@ -1,6 +1,10 @@
<template>
<div class="combo">
<div class="form" @click="showList" :class="{'error': value.status && value.status.hasError}">
<div
class="form"
@click="showList"
:class="{'error': value && value.status && value.status.hasError}"
>
{{currentValue}}
<i v-if="opened" class="fa fa-chevron-up" />
<i v-else class="fa fa-chevron-down" />
@@ -18,29 +18,43 @@ export default class Keyboard extends Vue {
@Prop()
value: string;
get Value() {
return this.actualValue.setpointHMI;
}
set Value(v: number) {
try {
let scale = Math.pow(10, this.actualValue.numDec);
this.actualValue.setpointHMI = Math.round(v * scale) / scale;
} catch{
this.actualValue.setpointHMI = v;
}
}
del() {
this.actualValue.setpointHMI = 0;
this.Value = 0;
}
canc() {
let temp = String(this.actualValue.setpointHMI);
this.actualValue.setpointHMI = Number(temp.slice(0, -1))
let temp = String(this.Value);
this.Value = Number(temp.slice(0, -1))
}
add(num: string) {
if (this.point) {
this.point += num;
this.actualValue.setpointHMI = Number(this.point)
this.Value = Number(this.point)
} else {
let temp = String(this.actualValue.setpointHMI);
let temp = String(this.Value);
temp += num;
this.actualValue.setpointHMI = Number(temp)
this.Value = Number(temp)
}
}
addpoint() {
if (!this.point) {
this.point = String(this.actualValue.setpointHMI);
this.point = String(this.Value);
this.point += ".";
}
}
@@ -29,6 +29,19 @@ export default class Numeric extends Vue {
focused: boolean = false;
get Value() {
return this.value.setpointHMI;
}
set Value(v: number) {
try {
let scale = Math.pow(10, this.value.numDec);
this.value.setpointHMI = Math.round(v * scale) / scale;
} catch {
this.value.setpointHMI = v;
}
}
onFocus() {
if (this.value && this.value.status && !this.value.status.enabled) return;
let rect = this.$el.getBoundingClientRect();
@@ -6,7 +6,7 @@
<input
type="number"
ref="input"
v-model.number="value.setpointHMI"
v-model.number="Value"
@focus="onFocus"
@blur="onBlur"
:id="id"
@@ -4,6 +4,7 @@ import { Prop } from 'vue-property-decorator';
import { Hub } from "@/services";
import Numeric from "./numeric";
import { unitOfTime } from "moment";
@Component({ name: "slider" })
export default class Slider extends Vue {
@@ -33,19 +34,29 @@ export default class Slider extends Vue {
this.value.setpointHMI = v;
}
get step() {
// var s = ((this.value.range.max - this.value.range.min) / (this.lines + 1));
// var m = Math.pow(10, this.decimal);
// return Math.round(s * m) / m
return 1;
};
@Prop({ default: .2 })
incrementingStep: number;
step: number = 1;
// get step() {
// var s = ((this.value.range.max - this.value.range.min) / (this.lines + 1));
// var m = Math.pow(10, this.decimal);
// return Math.round(s * m) / m
// return 1;
// };
mounted() {
this.step = 1 / Math.pow(10, this.value.numDec);
if (Number.isNaN(this.step)) this.step = 1;
}
startIncrement() {
if (!this.value.status.enabled) return;
this.incrementing = setInterval(() => {
var v = this.value.setpointHMI;
if (v < this.value.range.max) {
v += this.step;
v += Math.floor(this.step);
this.step += this.incrementingStep;
}
if (v > this.value.range.max) {
v = this.value.range.max;
@@ -59,7 +70,8 @@ export default class Slider extends Vue {
this.incrementing = setInterval(() => {
var v = this.value.setpointHMI;
if (v > this.value.range.min) {
v -= this.step;
v -= Math.floor(this.step);
this.step += this.incrementingStep;
}
if (v < this.value.range.min) {
v = this.value.range.min;
@@ -74,7 +86,7 @@ export default class Slider extends Vue {
clearInterval(this.incrementing);
this.incrementing = 0;
this.step = 1;
};
@@ -1,7 +1,13 @@
<template>
<div class="slider-container">
<div class="slider">
<button @mousedown="startDecrement()" @mouseup="confirm()" @mouseout="confirm()">
<button
@mousedown="startDecrement()"
v-on:touchstart="startDecrement()"
v-on:touchend="confirm()"
@mouseup="confirm()"
@mouseout="confirm()"
>
<img src="assets/icons/png/min.png" />
</button>
<div class="control">
@@ -21,7 +27,13 @@
<small>{{`${this.value.range.max} ${this.value.unitMeasure}`}}</small>
</div>
</div>
<button @mousedown="startIncrement()" @mouseup="confirm()" @mouseout="confirm()">
<button
@mousedown="startIncrement()"
v-on:touchstart="startIncrement()"
v-on:touchend="confirm()"
@mouseup="confirm()"
@mouseout="confirm()"
>
<img src="assets/icons/png/max.png" />
</button>
</div>
@@ -40,5 +52,13 @@
justify-items: center;
width: 100%;
}
input[type="range"] {
pointer-events: none;
}
input[type="range"]::-webkit-slider-thumb {
pointer-events: auto;
}
</style>
<script lang="ts" src="./slider.ts"></script>
@@ -4,7 +4,7 @@ import { Prop, Watch } from 'vue-property-decorator';
import { messageService } from '@/_base';
import AppRibbon from "@/components/app-ribbon.vue";
import { alarmList } from "@/app_modules/alarms";
import { AppModel, appModelActions } from '@/store';
import { AppModel, appModelActions, store } from '@/store';
import { getColorFromName, isDarkColor } from "@/_base/utils";
import ArchInterface from "../components/arch-interface/arch-interface.vue";
import gauge from "./base-components/gauge.vue";
@@ -12,6 +12,8 @@ import { prodService } from '@/services/prodService';
import moment from 'moment';
import stats from "./base-components/stats.vue";
import hitem from "./base-components/item.vue";
import { Hub } from '@/services';
import { SoftKeysConfigurationModel } from '@/store/machineInfo.store';
@Component({
components: {
@@ -25,7 +27,6 @@ import hitem from "./base-components/item.vue";
})
export default class Dashboard extends Vue {
get panel() {
return (this.$store.state as AppModel).prod.panel;
}
@@ -54,7 +55,7 @@ export default class Dashboard extends Vue {
}
get remainingTime() {
return this.endEstimation.diff(this.now);
return Math.max(this.endEstimation.diff(this.now), 0);
}
get currentUser() {
@@ -65,6 +66,18 @@ export default class Dashboard extends Vue {
return (this.$store.state as AppModel).machineInfo.cmsConnectReady;
}
get softKeys(): { [id: number]: SoftKeysConfigurationModel[] } {
return store.state.machineInfo.softKeys as { [id: number]: SoftKeysConfigurationModel[] };
}
get allSoftKeys(): SoftKeysConfigurationModel[] {
var result = [];
for (const key in this.softKeys) {
const element = this.softKeys[key];
result.push(...element);
}
return result;
}
loading = false;
async loadMore() {
@@ -80,11 +93,11 @@ export default class Dashboard extends Vue {
getColor(nome, cognome) {
return getColorFromName(nome, cognome);
}
isDarkColor(color) {
return isDarkColor(color);
}
async mounted() {
prodService.GetProdPanel();
@@ -95,7 +108,6 @@ export default class Dashboard extends Vue {
}
public get ribbonStatus(): ribbonStatusEnum {
// Controllo se ci sono allarmi
let s = this.$store.state as AppModel;
@@ -103,7 +115,6 @@ export default class Dashboard extends Vue {
if (s.alarms.warnings.length > 0) return ribbonStatusEnum.Warning;
if (s.process.running) return ribbonStatusEnum.Working;
return ribbonStatusEnum.Idle;
}
public get alarmTitle() {
@@ -120,7 +131,6 @@ export default class Dashboard extends Vue {
default:
return "";
}
}
public get alarmCount() {
@@ -148,6 +158,13 @@ export default class Dashboard extends Vue {
}
}
sendSoftKey(id: string) {
debugger
var sk = this.allSoftKeys.find(s => s.refCallParam == id);
if (sk)
Hub.Current.sendUserSoftKey(sk.id);
}
public sendMessage(name: string) {
messageService.publishToChannel(name);
}
@@ -155,7 +172,6 @@ export default class Dashboard extends Vue {
close() {
appModelActions.ShowDashboard(this.$store);
};
}
enum ribbonStatusEnum {
@@ -99,7 +99,12 @@
</div>
<div class="end">
<small>{{'dashboard-timing-end' | localize('fine')}}</small>
<time v-if="panel.stimaDurata">~{{endEstimation | date('HH:mm')}}</time>
<time v-if="panel.stimaDurata">
<span>~{{endEstimation | date('HH:mm')}}</span>
<small
style="text-transform:lowercase"
>+{{Math.floor(remainingTime /1000 / 3600 /24)}} {{'remainging__days' | localize("gg")}}</small>
</time>
<time v-else>--:--</time>
</div>
</div>
@@ -107,14 +112,14 @@
<div class="setpoint">
<img src="assets/icons/png/inv.png" />
<label>{{panel.tempAct}}°C</label>
<button>
<button @click="sendSoftKey('dash_setpoint_minus')">
<i class="fa fa-minus"></i>
</button>
<div>
<small>{{'dashboard-setpoint' | localize('set point')}}</small>
<span>{{panel.tempSetpoint}}°C</span>
</div>
<button>
<button @click="sendSoftKey('dash_setpoint_plus')">
<i class="fa fa-plus"></i>
</button>
</div>
@@ -124,7 +129,9 @@
<gauge></gauge>
<div class="remaining_time">
<small>{{'dashboard-remaining-time' | localize('tempo rimanente')}}</small>
<time v-if="panel.stimaDurata">{{remainingTime | date('HH:mm:ss')}}</time>
<time
v-if="panel.stimaDurata"
>{{Math.floor(remainingTime /1000 / 3600)}}:{{remainingTime | date('mm:ss')}}</time>
<time v-else>--:--:--</time>
</div>
</div>
@@ -4,6 +4,19 @@ import { Prop, InjectReactive } from 'vue-property-decorator';
import { IGanttOptions } from './gantt';
import { store } from '@/store';
import { RecipeGetters } from '@/store/recipe.store';
import { ModalHelper } from '@/components/modals';
import ShowRiscaldamentoInfo from "@/app_modules_thermo/setup/riscaldi/components/show-riscaldi-info.vue";
import ShowFormatoInfo from "@/app_modules_thermo/setup/formato/components/show-formato-info.vue";
import ShowPirometroInfo from "@/app_modules_thermo/setup/pirometro/components/show-pirometro-info.vue";
import ShowCicloInfo from "@/app_modules_thermo/setup/ciclo/components/show-ciclo-info.vue";
import ShowRaffreddamentoInfo from "@/app_modules_thermo/setup/raffreddamento/components/show-raffreddamento-info.vue";
import ShowControstampoInfo from "@/app_modules_thermo/setup/controstampo_setup/components/show-controstampo-info.vue";
import ShowQuoteVelocitaInfo from "@/app_modules_thermo/setup/quote-velocita/components/show-quote-velocita-info.vue";
import ShowEstrazioneInfo from "@/app_modules_thermo/setup/estrazione/components/show-estrazione-info.vue";
import ShowVuotoInfo from "@/app_modules_thermo/setup/vuoto/show-vuoto-info.vue";
import ShowImbutituraInfo from "@/app_modules_thermo/setup/imbutitura/show-imbutitura-info.vue";
import ShowOpzioniInfo from "@/app_modules_thermo/setup/opzioni/show-opzioni-info.vue";
@Component({})
export default class GanttComponent extends Vue {
@@ -71,6 +84,53 @@ export default class GanttComponent extends Vue {
return this.showStatus ? this.ganttOptions.block_status_minDuration : 0;
}
openModal() {
if (this.value.category)
switch (this.value.category.toLowerCase()) {
case "general": this.showModalAtStep(0, this.value.subCategory_1); break;
case "positions": this.showModalAtStep(1, this.value.subCategory_1); break;
case "cycle": this.showModalAtStep(2, this.value.subCategory_1); break;
case "heats": this.showModalAtStep(3, this.value.subCategory_1); break;
case "pyrometer": this.showModalAtStep(4, this.value.subCategory_1); break;
case "drawing": this.showModalAtStep(5, this.value.subCategory_1); break;
case "upperplate": this.showModalAtStep(6, this.value.subCategory_1); break;
case "cooling": this.showModalAtStep(7, this.value.subCategory_1); break;
case "vacuum": this.showModalAtStep(8, this.value.subCategory_1); break;
case "extraction": this.showModalAtStep(9, this.value.subCategory_1); break;
case "options": this.showModalAtStep(10, this.value.subCategory_1); break;
}
}
async showModalAtStep(step: number, subcategory: string) {
try {
let next = await this.showModalStep(step, subcategory);
if (next == null) return;
this.showModalAtStep(step + next, null);
} catch {
}
}
showModalStep(step: number, subcategory: string): Promise<number> {
switch (step) {
case 0: return ModalHelper.ShowModalAsync(ShowFormatoInfo, { subcategory });
case 1: return ModalHelper.ShowModalAsync(ShowQuoteVelocitaInfo, { subcategory });
case 2: return ModalHelper.ShowModalAsync(ShowCicloInfo, { subcategory });
case 3: return ModalHelper.ShowModalAsync(ShowRiscaldamentoInfo, { subcategory });
case 4: return ModalHelper.ShowModalAsync(ShowPirometroInfo, { subcategory });
case 5: return ModalHelper.ShowModalAsync(ShowImbutituraInfo, { subcategory });
case 6: return ModalHelper.ShowModalAsync(ShowControstampoInfo, { subcategory });
case 7: return ModalHelper.ShowModalAsync(ShowRaffreddamentoInfo, { subcategory });
case 8: return ModalHelper.ShowModalAsync(ShowVuotoInfo, { subcategory });
case 9: return ModalHelper.ShowModalAsync(ShowEstrazioneInfo, { subcategory });
case 10: return ModalHelper.ShowModalAsync(ShowOpzioniInfo, { subcategory });
// case 11: return ModalHelper.ShowModalAsync(AvvioProduzione);
}
return null;
}
// mounted() {
// this.value.showDelay = true;
// }
@@ -36,7 +36,7 @@
x="0"
y="0"
>
<div class="body-header">{{value.label | localize(value.label)}}</div>
<div class="body-header" @click="openModal()">{{value.label | localize(value.label)}}</div>
</foreignObject>
<foreignObject
:width="(duration - (showStatus && recipeValue?statusDuration:0)) * ganttOptions.secondSize"
@@ -149,7 +149,7 @@
</foreignObject>
</g>
<g v-if="value.showDelay">
<g v-if="value.showDelay && !ganttOptions.printing">
<line x1="0" y1="0" y2="0" :x2="actualDelay * ganttOptions.secondSize" class="progress-line" />
<foreignObject
width="30"
@@ -161,7 +161,10 @@
</foreignObject>
</g>
<g :transform="`translate(${delayDuration * ganttOptions.secondSize} 0)`">
<g
:transform="`translate(${delayDuration * ganttOptions.secondSize} 0)`"
v-if="!ganttOptions.printing"
>
<line
x1="0"
y1="0"
@@ -32,6 +32,14 @@ export default class GanttRow extends Vue {
return Array.from(this.blocks.values()).filter(i => i.section == this.section).sort((a, b) => a.priority - b.priority);
}
get maxVerticalPosition() {
return Math.max(...this.blocks.map(b => this.verticalPosition(b)))
}
get maxBlockPosition() {
return Math.max(...this.blocks.map(b => this.startPosition(b)))
}
startPosition(block: server.Modblock) {
return blockStartPosition(block, this.blocks, this.ganttOptions) * this.ganttOptions.secondSize;
}
@@ -65,7 +73,8 @@ export default class GanttRow extends Vue {
// cerco la prima delle righe disponibili che avrebbero posto per il mio blocco
// una riga è disponibile se non ci sono blocchi che occupano lo spazio che mi servirebbe
let myrow = rows.filter(r => r.filter(b => (b.from <= myStartPosition && b.to >= myStartPosition) || (b.from <= myEndPosition && b.to >= myEndPosition)).length == 0).shift();
let myrow = rows.filter(r => r.filter(b => (b.from <= myStartPosition && b.to >= myStartPosition) || (b.from <= myEndPosition && b.to >= myEndPosition) ||
(myStartPosition <= b.from && myEndPosition >= b.from) || (myStartPosition <= b.to && myEndPosition >= b.to)).length == 0).shift();
// se non ho trovato una riga allora non c'è posto oppure non ci sono righe..
// aggiungo eventualmente una nuova riga
@@ -120,6 +129,7 @@ function blockDuration(block: server.Modblock, options: IGanttOptions, includeDe
duration += Math.max(minduration, block.terminated ? 0 : block.estimatedDuration, block.actualDuration);
if (block.idParam > 0 && includeDelay)
duration += options.block_status_minDuration;
@@ -4,6 +4,24 @@ import moment from "moment";
import ganttHeader from "./gantt-header.vue";
import ganttRow from "./gantt-row.vue"
import timeLine from "./timeline.vue";
import { recipeService } from "@/services/recipeService";
import { store, AppModel } from "@/store";
var ContainerElements = ["svg", "g", "foreignobject"];
var RelevantStyles = {
"rect": ["fill", "stroke", "stroke-width", "rx", "ry"],
"path": ["fill", "stroke", "stroke-width"],
"circle": ["fill", "stroke", "stroke-width"],
"line": ["stroke", "stroke-width"],
"text": ["fill", "font-size", "text-anchor"],
"polygon": ["stroke", "fill"],
"foreignobject": [],
"div": [
"align-items", "justify-content", "font-family", "flex-flow", "font-size",
"height", "padding", "color", "display", "width", "border-radius",
"background-color", "transform"],
"span": ["letter-spacing", "text-transform", "background-color"]
};
@Component({
components: {
@@ -18,12 +36,71 @@ export default class Gantt extends Vue {
padX: number = 0;
padY: number = 0;
get panel() {
return (this.$store.state as AppModel).prod.panel;
}
get ganttHeight() {
return (this.$refs.section1 as any).rowHeight +
(this.$refs.section2 as any).rowHeight +
(this.$refs.section3 as any).rowHeight;
}
public print() {
this.ganttOptions.printing = true;
this.$nextTick(() => {
var oDOM = (this.$refs.mainContainer as SVGElement).cloneNode(true)
this.exportSVGWithStyle(oDOM, this.$refs.mainContainer)
var data = new XMLSerializer().serializeToString(oDOM);
var DOMURL = (window.URL || window.webkitURL);
var svgBlob = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
// var url = DOMURL.createObjectURL(svgBlob);
recipeService.UploadImage(svgBlob, this.panel.nomeRicetta.replace('.rcp', '') + ".svg");
this.ganttOptions.printing = false;
// window.open(url, '_blank')
});
}
exportSVGWithStyle(ParentNode, OrigData) {
var Children = ParentNode.childNodes;
var OrigChildDat = OrigData.childNodes;
for (var cd = 0; cd < Children.length; cd++) {
var Child = Children[cd];
var TagName = (Child.tagName as string)?.toLowerCase();
if (ContainerElements.indexOf(TagName) != -1) {
this.exportSVGWithStyle(Child, OrigChildDat[cd])
} else if (TagName in RelevantStyles) {
var StyleDef = window.getComputedStyle(OrigChildDat[cd]);
var StyleString = "";
for (var st = 0; st < RelevantStyles[TagName].length; st++) {
StyleString += RelevantStyles[TagName][st] + ":" + StyleDef.getPropertyValue(RelevantStyles[TagName][st]) + "; ";
}
Child.setAttribute("style", StyleString);
}
}
}
get PadX() {
let w = ((this.$refs.mainContainer as any)?.clientWidth ?? 0) / 3;
if (this.follow && w && this.currentTime * this.ganttOptions.secondSize > w)
return -(this.currentTime * this.ganttOptions.secondSize - w);
return this.padX;
}
set PadX(value: number) {
if (!this.follow)
this.padX = value;
@@ -69,7 +146,8 @@ export default class Gantt extends Vue {
block_body_Vacuum_minDuration: 5,
block_body_Cooling_minDuration: 10,
block_body_Extraction_minDuration: 5,
block_padding: 0.01
block_padding: 0.01,
printing: false
};
@Prop({ default: 0 })
@@ -179,4 +257,5 @@ export interface IGanttOptions {
block_body_Cooling_minDuration: number;
block_body_Extraction_minDuration: number;
block_padding: number;
printing: boolean;
}
@@ -4,7 +4,7 @@
ref="mainContainer"
preserveAspectRation="xMinYMax"
width="100%"
height="100%"
:height="ganttOptions.printing ? `${ganttHeight}px` : '100%'"
@mousemove.capture="doPan"
v-on:touchmove="doPan"
@mousedown="startPan"
@@ -55,7 +55,7 @@
</g>
<gantt-header :padding-horizontal="PadX " :zoom-factor="zoomFactor" />
<time-line
v-if="!!getRowHeight($refs.section1) && !!getRowHeight($refs.section2)"
v-if="!!getRowHeight($refs.section1) && !!getRowHeight($refs.section2) && !ganttOptions.printing"
:padding-horizontal="PadX"
:zoom-factor="zoomFactor"
:speed="1"
@@ -1,15 +1,13 @@
<template>
<svg class="timeline">
<g :transform="`translate(${ paddingHorizontal} 0)`" class="pad">
<g :transform="`scale(${zoomFactor} 1)`">
<g :transform="`scale(${zoomFactor} 1)`">
<g :transform="`translate(${ paddingHorizontal} 0)`" class="pad">
<g
:transform="`translate(${position} 0)`"
:style="position>20?`transition: transform ${speed}s linear`:''"
>
<g :transform="`scale(${2-zoomFactor} 1)`">
<path class="arrow" d="M 0 0 L 30 0 L 15 24 Z" />
<line x1="15" y1="24" x2="15" :y2="lineHeight" />
</g>
<path class="arrow" d="M 0 0 L 30 0 L 15 24 Z" />
<line x1="15" y1="24" x2="15" :y2="lineHeight" />
</g>
</g>
</g>
@@ -0,0 +1,23 @@
import Vue from "vue";
import Component from "vue-class-component";
import gantt from "./gantt/gantt.vue";
import { store } from "@/store";
import { messageService } from "@/_base";
@Component({
components: {
gantt
}
})
export default class PrintProcesso extends Vue {
get blocks(): server.Modblock[] {
return store.state.modules.blocks;
}
mounted() {
messageService.subscribeToChannel("print", () => {
(this.$refs.gantt as any).print();
})
}
}
@@ -0,0 +1,6 @@
<template>
<div class="printContainer">
<gantt ref="gantt" v-if="blocks" :blocks="blocks" :zoom-factor="1"></gantt>
</div>
</template>
<script src="./printProcesso.ts" lang="ts">
@@ -19,7 +19,9 @@
@checkChanged="softKeyChanged(b.id, b.operatorConfirmationNeeded)"
></soft-key>
</template>
<label><span v-if="tot">{{actual}}/{{tot}}</span></label>
<label>
<span v-if="tot">{{actual}}/{{tot}}</span>
</label>
</div>
<gantt
ref="gantt"
@@ -4,13 +4,14 @@ import { Prop } from "vue-property-decorator";
import { Deferred } from "@/services";
import { Modal, ModalHelper } from "@/components/modals";
import { messageService } from "@/_base";
import { recipeService } from "@/services/recipeService";
@Component({ components: { modal: Modal } })
export default class Notes extends Vue {
@Prop()
deferred: Deferred<string>;
@Prop()
value: string;
note: string = null;
@@ -21,6 +22,10 @@ export default class Notes extends Vue {
this.note = value;
}
async mounted() {
this.Note = await recipeService.GetNote();
}
save(value: string) {
this.deferred.resolve(this.Note);
ModalHelper.HideModal();
@@ -3,7 +3,7 @@ import { Modal, ModalHelper } from "@/components/modals";
import { Factory, messageService, awaiter } from "@/_base";
import Component from "vue-class-component";
import { Prop } from 'vue-property-decorator';
import { appModelActions } from "@/store/app.store";
import { appModelActions, store } from "@/store/app.store";
import { prodService } from "@/services/prodService";
@Component({
@@ -13,7 +13,7 @@ import { prodService } from "@/services/prodService";
})
export default class AvvioProduzione extends Vue {
enabling: boolean = true;
newjob: boolean = true;
pieces: Recipe.IValue = {
setpointHMI: 0,
@@ -25,13 +25,27 @@ export default class AvvioProduzione extends Vue {
}
} as Recipe.IValue;
warmuppieces: Recipe.IValue = {
setpointHMI: 0,
range: { min: 0, max: 999999 },
status: {
enabled: true,
hasError: false,
visible: true
}
} as Recipe.IValue;
annulla() {
ModalHelper.HideModal();
};
get prod() { return store.state.prod.panel }
showDashboard() {
prodService.Start(this.pieces.setpointHMI);
prodService.Start(this.pieces.setpointHMI, this.newjob, this.warmuppieces.setpointHMI);
ModalHelper.HideModal();
appModelActions.ShowDashboard(this.$store);
@@ -53,4 +67,8 @@ export default class AvvioProduzione extends Vue {
ModalHelper.HideModal();
}
mounted() {
this.pieces.setpointHMI = this.prod.numTarget;
}
}
@@ -11,13 +11,36 @@
</div>
<section>
<article>
<div class="input-area mb-10">
<label>{{'enabling'|localize('Settaggio numero pezzi')}}</label>
<toggle-button v-model="enabling"></toggle-button>
<div class="input-area mb-10" style="margin:auto">
<label>{{'new'|localize('Nuova produzione')}}</label>
<toggle-button v-model="newjob"></toggle-button>
</div>
<div class="input-area" v-if="enabling">
<label>{{'mock_pieces'|localize('Numero pezzi')}}</label>
<numeric v-model="pieces" :keyboardPosition="'bottom'" />
<div class="box mt-10">
<div class="header">
<label>{{'warmup_cycles' | localize("Preriscaldo")}}</label>
</div>
<div class="body">
<div class="input-area mb-10">
<label>{{'warmup_cycles_pieces'|localize('Cicli preriscaldo')}}</label>
<numeric v-model="warmuppieces" :keyboardPosition="'bottom'" />
</div>
</div>
</div>
<div class="box mt-10">
<div class="header">
<label>{{'production_management' | localize("Gestione produzione")}}</label>
</div>
<article class="body">
<div class="input-area mb-10">
<label>{{'production_enabling'|localize('Modifica numero pezzi')}}</label>
<toggle-button v-model="pieces.status.enabled"></toggle-button>
</div>
<div class="input-area mb-10">
<label style="flex:1;">{{'mock_pieces'|localize('Numero pezzi')}}</label>
<label>{{prod.numDone}}/</label>
<numeric v-model="pieces" :keyboardPosition="'bottom'" />
</div>
</article>
</div>
</article>
</section>
@@ -16,11 +16,19 @@ import { debounce } from '@/_base/debounce';
export default class ShowCicloInfo extends Vue {
recipe: Recipe.IRecipe = this.$store.getters.getCurrent();
show: number = 0;
show: string = 'forming';
@Prop()
deferred: Deferred<boolean>;
@Prop()
value: string;
mounted() {
if (this.value)
this.show = this.value;
}
annulla() {
recipeService.Cancel();
// ModalHelper.HideModal();
@@ -79,13 +87,17 @@ export default class ShowCicloInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -2,10 +2,13 @@
<div class="setup">
<modal type="ciclo-info">
<div class="tab-header" slot="header-buttons">
<button :class="{'active':show==0}" @click="show=0">{{'cycle' | localize('Ciclo di formatura')}}</button>
<button
:class="{'active':show==1}"
@click="show=1"
:class="{'active':show=='forming'}"
@click="show='forming'"
>{{'cycle' | localize('Ciclo di formatura')}}</button>
<button
:class="{'active':show=='loader'}"
@click="show='loader'"
v-if="isLoaderEnabled()"
>{{'loader' | localize('Caricatore')}}</button>
</div>
@@ -14,8 +17,8 @@
<i class="fa fa-remove"></i>
</button>
</div>
<cicloformatura v-if="show==0" :recipe="recipe"></cicloformatura>
<caricatore v-if="show==1" :recipe="recipe"></caricatore>
<cicloformatura v-if="show=='forming'" :recipe="recipe"></cicloformatura>
<caricatore v-if="show=='loader'" :recipe="recipe"></caricatore>
<footer>
<button
class="btn"
@@ -15,7 +15,7 @@ import ShowEstrazioneInfo from "@/app_modules_thermo/setup/estrazione/components
import ShowVuotoInfo from "@/app_modules_thermo/setup/vuoto/show-vuoto-info.vue";
import ShowImbutituraInfo from "@/app_modules_thermo/setup/imbutitura/show-imbutitura-info.vue";
import ShowOpzioniInfo from "@/app_modules_thermo/setup/opzioni/show-opzioni-info.vue";
import AvvioProduzione from "@/app_modules_thermo/setup/avvio-produzione/avvio-produzione.vue";
// import AvvioProduzione from "@/app_modules_thermo/setup/avvio-produzione/avvio-produzione.vue";
import { store } from '@/store';
@@ -54,7 +54,7 @@ export default class Setup extends Vue {
case 8: return ModalHelper.ShowModalAsync(ShowVuotoInfo);
case 9: return ModalHelper.ShowModalAsync(ShowEstrazioneInfo);
case 10: return ModalHelper.ShowModalAsync(ShowOpzioniInfo);
case 11: return ModalHelper.ShowModalAsync(AvvioProduzione);
// case 11: return ModalHelper.ShowModalAsync(AvvioProduzione);
}
return null;
@@ -79,9 +79,9 @@
:status="overview.options"
></setup-button>
<div class="setup-play" @click="showModalAtStep(11);">
<!-- <div class="setup-play" @click="showModalAtStep(11);">
<img class="dim" src="assets/icons/svg/setup-play.svg" />
</div>
</div>-->
</div>
</modal>
</div>
@@ -27,11 +27,19 @@ import { debounce } from '@/_base/debounce';
export default class ShowControstampoInfo extends Vue {
recipe: Recipe.IRecipe = this.$store.getters.getCurrent();
show: number = 0;
show: string = 'cycle';
@Prop()
deferred: Deferred<boolean>;
@Prop()
value: string;
mounted() {
if (this.value)
this.show = this.value;
}
annulla() {
recipeService.Cancel();
// ModalHelper.HideModal();
@@ -94,13 +102,17 @@ export default class ShowControstampoInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -3,23 +3,23 @@
<modal type="controstamposetup-info">
<div class="tab-header" slot="header-buttons">
<button
:class="{'active':show == 0}"
@click="show=0"
:class="{'active':show == 'cycle'}"
@click="show='cycle'"
v-if="isCycleEnabled()"
>{{'upperplate_cycle' |localize('Ciclo controstampo')}}</button>
<button
:class="{'active':show == 1}"
@click="show=1"
:class="{'active':show == 'air'}"
@click="show='air'"
v-if="isAirEnabled()"
>{{'upperplate_air' |localize('Aria controstampo')}}</button>
<button
:class="{'active':show == 2}"
@click="show=2"
:class="{'active':show == 'vacuum'}"
@click="show='vacuum'"
v-if="isVacuumEnabled()"
>{{'upperplate_vacuum' |localize('Vuoto controstampo')}}</button>
<button
:class="{'active':show == 3}"
@click="show=3"
:class="{'active':show == 'extraction'}"
@click="show='extraction'"
v-if="isExtractionEnabled()"
>{{'upperplate_extraction' |localize('Estrazione controstampo')}}</button>
</div>
@@ -28,10 +28,10 @@
<i class="fa fa-remove"></i>
</button>
</div>
<ciclocontrostampo v-if="show==0" :recipe="recipe"></ciclocontrostampo>
<ariacontrostampo v-if="show==1" :recipe="recipe"></ariacontrostampo>
<vuotocontrostampo v-if="show==2" :recipe="recipe"></vuotocontrostampo>
<estrazionecontrostampo v-if="show==3" :recipe="recipe"></estrazionecontrostampo>
<ciclocontrostampo v-if="show=='cycle'" :recipe="recipe"></ciclocontrostampo>
<ariacontrostampo v-if="show=='air'" :recipe="recipe"></ariacontrostampo>
<vuotocontrostampo v-if="show=='vacuum'" :recipe="recipe"></vuotocontrostampo>
<estrazionecontrostampo v-if="show=='extraction'" :recipe="recipe"></estrazionecontrostampo>
<footer>
<button
class="btn"
@@ -74,13 +74,17 @@ export default class ShowEstrazioneInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -4,7 +4,7 @@
<div class="borded_label" id="quota1" v-focus-on:general_sizes_upperplate_max_height>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_upperplate_max_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_upperplate_max_height.valueAct}}</span>
<small>{{recipe.general_sizes_upperplate_max_height.unitMeasure}}</small>
</div>
</div>
@@ -4,14 +4,14 @@
<div class="borded_label" id="quota1" v-focus-on:general_sizes_frame_dim_y>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_frame_dim_y.setpointHMI}}</span>
<span>{{recipe.general_sizes_frame_dim_y.valueAct}}</span>
<small>{{recipe.general_sizes_frame_dim_y.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota2" v-focus-on:general_sizes_frame_dim_x>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_frame_dim_x.setpointHMI}}</span>
<span>{{recipe.general_sizes_frame_dim_x.valueAct}}</span>
<small>{{recipe.general_sizes_frame_dim_x.unitMeasure}}</small>
</div>
</div>
@@ -4,21 +4,21 @@
<div class="borded_label" id="quota1" v-focus-on:general_sizes_sheet_dim_x>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_sheet_dim_x.setpointHMI}}</span>
<span>{{recipe.general_sizes_sheet_dim_x.valueAct}}</span>
<small>{{recipe.general_sizes_sheet_dim_x.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota2" v-focus-on:general_sizes_sheet_dim_y>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_sheet_dim_y.setpointHMI}}</span>
<span>{{recipe.general_sizes_sheet_dim_y.valueAct}}</span>
<small>{{recipe.general_sizes_sheet_dim_y.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota3" v-focus-on:general_sizes_sheet_thickness>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_sheet_thickness.setpointHMI}}</span>
<span>{{recipe.general_sizes_sheet_thickness.valueAct}}</span>
<small>{{recipe.general_sizes_sheet_thickness.unitMeasure}}</small>
</div>
</div>
@@ -4,14 +4,14 @@
<div class="borded_label" id="quota1" v-focus-on:general_sizes_plate_dim_y>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_plate_dim_y.setpointHMI}}</span>
<span>{{recipe.general_sizes_plate_dim_y.valueAct}}</span>
<small>{{recipe.general_sizes_plate_dim_y.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota2" v-focus-on:general_sizes_plate_dim_x>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_plate_dim_x.setpointHMI}}</span>
<span>{{recipe.general_sizes_plate_dim_x.valueAct}}</span>
<small>{{recipe.general_sizes_plate_dim_x.unitMeasure}}</small>
</div>
</div>
@@ -4,35 +4,35 @@
<div class="borded_label" id="quota1" v-focus-on:general_sizes_mould_dim_x>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_dim_x.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_dim_x.valueAct}}</span>
<small>{{recipe.general_sizes_mould_dim_x.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota2" v-focus-on:general_sizes_mould_dim_y>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_dim_y.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_dim_y.valueAct}}</span>
<small>{{recipe.general_sizes_mould_dim_y.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota3" v-focus-on:general_sizes_mould_max_height>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_max_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_max_height.valueAct}}</span>
<small>{{recipe.general_sizes_mould_max_height.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota4" v-focus-on:general_sizes_mould_base_height>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_base_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_base_height.valueAct}}</span>
<small>{{recipe.general_sizes_mould_base_height.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota5" v-focus-on:general_sizes_mould_min_height>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_min_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_min_height.valueAct}}</span>
<small>{{recipe.general_sizes_mould_min_height.unitMeasure}}</small>
</div>
</div>
@@ -101,6 +101,10 @@ export default class ShowFormatoInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
@@ -83,6 +83,10 @@ export default class ShowImbutituraInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
@@ -165,6 +165,10 @@ export default class ShowOpzioniInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
@@ -17,11 +17,19 @@ import { debounce } from "@/_base/debounce";
export default class ShowPirometroInfo extends Vue {
recipe: Recipe.IRecipe = this.$store.getters.getCurrent();
show: number = 0;
show: string = 'pyrometer';
@Prop()
deferred: Deferred<boolean>;
@Prop()
value: string;
mounted() {
if (this.value)
this.show = this.value;
}
annulla() {
recipeService.Cancel();
// ModalHelper.HideModal();
@@ -75,13 +83,17 @@ export default class ShowPirometroInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -2,15 +2,15 @@
<div class="setup">
<modal type="pirometro-info">
<div class="tab-header" slot="header-buttons">
<button :class="{'active':show==0}" @click="show=0">Pirometro</button>
<button :class="{'active':show=='pyrometer'}" @click="show='pyrometer'">Pirometro</button>
<button
:class="{'active':show==1}"
@click="show=1"
:class="{'active':show=='upperthermoregulator'}"
@click="show='upperthermoregulator'"
v-if="isUpperthermoregulatorEnabled()"
>{{'upperthermoregulator' | localize('Termoregolazione riscaldo superiore')}}</button>
<button
:class="{'active':show==2}"
@click="show=2"
:class="{'active':show=='lowerthermoregulator'}"
@click="show='lowerthermoregulator'"
v-if="isLowerthermoregulatorEnabled()"
>{{'lowerthermoregulator' | localize('Termoregolazione riscaldo inferiore')}}</button>
</div>
@@ -19,9 +19,9 @@
<i class="fa fa-remove"></i>
</button>
</div>
<pirometro v-if="show==0" :recipe="recipe"></pirometro>
<termosuperiore v-if="show==1" :recipe="recipe"></termosuperiore>
<termoinferiore v-if="show==2" :recipe="recipe"></termoinferiore>
<pirometro v-if="show=='pyrometer'" :recipe="recipe"></pirometro>
<termosuperiore v-if="show=='upperthermoregulator'" :recipe="recipe"></termosuperiore>
<termoinferiore v-if="show=='lowerthermoregulator'" :recipe="recipe"></termoinferiore>
<footer>
<button
class="btn"
@@ -4,24 +4,24 @@
<div class="borded_label" id="quota1">
<div v-focus-on:positions_frame_intermediate_position>
<img src="assets/icons/png/salita.png" />
<span>{{recipe.positions_frame_intermediate_position.setpointHMI}}</span>
<span>{{recipe.positions_frame_intermediate_position.valueAct}}</span>
<small>{{recipe.positions_frame_intermediate_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_frame_intermediate_speed>
<img src="assets/icons/png/bassa.png" />
<span>{{recipe.positions_frame_intermediate_speed.setpointHMI}}</span>
<span>{{recipe.positions_frame_intermediate_speed.valueAct}}</span>
<small>{{recipe.positions_frame_intermediate_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota5">
<div v-focus-on:positions_frame_lower_position>
<img src="assets/icons/png/discesa.png" />
<span>{{recipe.positions_frame_lower_position.setpointHMI}}</span>
<span>{{recipe.positions_frame_lower_position.valueAct}}</span>
<small>{{recipe.positions_frame_lower_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_frame_lower_speed>
<img src="assets/icons/png/alta.png" />
<span>{{recipe.positions_frame_lower_speed.setpointHMI}}</span>
<span>{{recipe.positions_frame_lower_speed.valueAct}}</span>
<small>{{recipe.positions_frame_lower_speed.unitMeasure}}</small>
</div>
</div>
@@ -4,35 +4,29 @@
<div class="borded_label" id="quota1" v-focus-on:positions_mould_intermediate_position>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.positions_mould_intermediate_position.setpointHMI}}</span>
<span>{{recipe.positions_mould_intermediate_position.valueAct}}</span>
<small>{{recipe.positions_mould_intermediate_position.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota2" v-focus-on>
<div>
<img src="assets/icons/png/quota.png" />
<span>00</span>
<small>mm</small>
</div>
</div>
<div class="borded_label" id="quota3" v-focus-on>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_min_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_min_height.valueAct}}</span>
<small>{{recipe.general_sizes_mould_min_height.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota4" v-focus-on>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_base_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_base_height.valueAct}}</span>
<small>{{recipe.general_sizes_mould_base_height.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota5" v-focus-on>
<div>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_mould_max_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_mould_max_height.valueAct}}</span>
<small>{{recipe.general_sizes_mould_max_height.unitMeasure}}</small>
</div>
</div>
@@ -40,48 +34,48 @@
<div class="borded_label" id="quota6">
<div v-focus-on:positions_mould_upperdeceleration_position>
<img src="assets/icons/png/salita.png" />
<span>{{recipe.positions_mould_upperdeceleration_position.setpointHMI}}</span>
<span>{{recipe.positions_mould_upperdeceleration_position.valueAct}}</span>
<small>{{recipe.positions_mould_upperdeceleration_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_mould_upperdeceleration_speed>
<img src="assets/icons/png/bassa.png" />
<span>{{recipe.positions_mould_upperdeceleration_speed.setpointHMI}}</span>
<span>{{recipe.positions_mould_upperdeceleration_speed.valueAct}}</span>
<small>{{recipe.positions_mould_upperdeceleration_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota7">
<div v-focus-on:positions_mould_upper_position>
<img src="assets/icons/png/salita.png" />
<span>{{recipe.positions_mould_upper_position.setpointHMI}}</span>
<span>{{recipe.positions_mould_upper_position.valueAct}}</span>
<small>{{recipe.positions_mould_upper_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_mould_upper_speed>
<img src="assets/icons/png/alta.png" />
<span>{{recipe.positions_mould_upper_speed.setpointHMI}}</span>
<span>{{recipe.positions_mould_upper_speed.valueAct}}</span>
<small>{{recipe.positions_mould_upper_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota8">
<div v-focus-on:positions_mould_lowerdeceleration_position>
<img src="assets/icons/png/discesa.png" />
<span>{{recipe.positions_mould_lowerdeceleration_position.setpointHMI}}</span>
<span>{{recipe.positions_mould_lowerdeceleration_position.valueAct}}</span>
<small>{{recipe.positions_mould_lowerdeceleration_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_mould_lowerdeceleration_speed>
<img src="assets/icons/png/bassa.png" />
<span>{{recipe.positions_mould_lowerdeceleration_speed.setpointHMI}}</span>
<span>{{recipe.positions_mould_lowerdeceleration_speed.valueAct}}</span>
<small>{{recipe.positions_mould_lowerdeceleration_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota9">
<div v-focus-on:positions_mould_lower_position>
<img src="assets/icons/png/discesa.png" />
<span>{{recipe.positions_mould_lower_position.setpointHMI}}</span>
<span>{{recipe.positions_mould_lower_position.valueAct}}</span>
<small>{{recipe.positions_mould_lower_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_mould_lower_speed>
<img src="assets/icons/png/alta.png" />
<span>{{recipe.positions_mould_lower_speed.setpointHMI}}</span>
<span>{{recipe.positions_mould_lower_speed.valueAct}}</span>
<small>{{recipe.positions_mould_lower_speed.unitMeasure}}</small>
</div>
</div>
@@ -89,13 +83,9 @@
</template>
<style scoped>
#quota1 {
top: 140px;
top: calc(140px + 260px);
left: 300px;
}
#quota2 {
top: 540px;
left: 100px;
}
#quota3 {
top: 455px;
left: 670px;
@@ -109,19 +99,19 @@
left: 670px;
}
#quota9 {
top: 255px;
top: calc(255px + 260px);
left: 485px;
}
#quota8 {
top: 140px;
top: calc(140px + 260px);
left: 510px;
}
#quota6 {
top: 120px;
top: calc(120px + 260px);
left: 20px;
}
#quota7 {
top: 230px;
top: calc(230px + 260px);
left: 100px;
}
</style>
@@ -4,55 +4,55 @@ e<template>
<div class="borded_label" id="quota3">
<div v-focus-on:positions_upperplate_lower_position>
<img src="assets/icons/png/discesa.png" />
<span>{{recipe.positions_upperplate_lower_position.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_lower_position.valueAct}}</span>
<small>{{recipe.positions_upperplate_lower_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_upperplate_lower_speed>
<img src="assets/icons/png/alta.png" />
<span>{{recipe.positions_upperplate_lower_speed.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_lower_speed.valueAct}}</span>
<small>{{recipe.positions_upperplate_lower_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota4">
<div v-focus-on:positions_upperplate_lowerdeceleration_position>
<img src="assets/icons/png/discesa.png" />
<span>{{recipe.positions_upperplate_lowerdeceleration_position.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_lowerdeceleration_position.valueAct}}</span>
<small>{{recipe.positions_upperplate_lowerdeceleration_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_upperplate_lowerdeceleration_speed>
<img src="assets/icons/png/bassa.png" />
<span>{{recipe.positions_upperplate_lowerdeceleration_speed.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_lowerdeceleration_speed.valueAct}}</span>
<small>{{recipe.positions_upperplate_lowerdeceleration_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota1">
<div v-focus-on:positions_upperplate_upper_position>
<img src="assets/icons/png/salita.png" />
<span>{{recipe.positions_upperplate_upper_position.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_upper_position.valueAct}}</span>
<small>{{recipe.positions_upperplate_upper_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_upperplate_upper_speed>
<img src="assets/icons/png/alta.png" />
<span>{{recipe.positions_upperplate_upper_speed.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_upper_speed.valueAct}}</span>
<small>{{recipe.positions_upperplate_upper_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota2">
<div v-focus-on:positions_upperplate_upperdeceleration_position>
<img src="assets/icons/png/salita.png" />
<span>{{recipe.positions_upperplate_upperdeceleration_position.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_upperdeceleration_position.valueAct}}</span>
<small>{{recipe.positions_upperplate_upperdeceleration_position.unitMeasure}}</small>
</div>
<div v-focus-on:positions_upperplate_upperdeceleration_speed>
<img src="assets/icons/png/bassa.png" />
<span>{{recipe.positions_upperplate_upperdeceleration_speed.setpointHMI}}</span>
<span>{{recipe.positions_upperplate_upperdeceleration_speed.valueAct}}</span>
<small>{{recipe.positions_upperplate_upperdeceleration_speed.unitMeasure}}</small>
</div>
</div>
<div class="borded_label" id="quota5">
<div v-focus-on:general_sizes_upperplate_max_height>
<img src="assets/icons/png/quota.png" />
<span>{{recipe.general_sizes_upperplate_max_height.setpointHMI}}</span>
<span>{{recipe.general_sizes_upperplate_max_height.valueAct}}</span>
<small>{{recipe.general_sizes_upperplate_max_height.unitMeasure}}</small>
</div>
</div>
@@ -25,11 +25,20 @@ import { debounce } from "@/_base/debounce";
export default class ShowQuoteVelocitaInfo extends Vue {
recipe: Recipe.IRecipe = this.$store.getters.getCurrent();
show: number = 0;
show: string = 'mould';
@Prop()
deferred: Deferred<boolean>;
@Prop()
value: string;
mounted() {
if (this.value)
this.show = this.value;
}
annulla() {
recipeService.Cancel();
// ModalHelper.HideModal();
@@ -88,13 +97,17 @@ export default class ShowQuoteVelocitaInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -3,18 +3,18 @@
<modal type="quote-velocita-info">
<div class="tab-header" slot="header-buttons">
<button
:class="{'active':show == 0}"
@click="show=0"
:class="{'active':show == 'mould'}"
@click="show='mould'"
v-if="isMouldEnabled()"
>{{'mould' | localize("Stampo")}}</button>
<button
:class="{'active':show == 1}"
@click="show=1"
:class="{'active':show == 'frame'}"
@click="show='frame'"
v-if="isFrameEnabled()"
>{{'frame' | localize("Cornice")}}</button>
<button
:class="{'active':show == 2}"
@click="show=2"
:class="{'active':show == 'upperplate'}"
@click="show='upperplate'"
v-if="isUpperplateEnabled()"
>{{'upperplate' | localize("Controstampo")}}</button>
</div>
@@ -23,9 +23,9 @@
<i class="fa fa-remove"></i>
</button>
</div>
<salita-stampo v-if="show==0" :recipe="recipe"></salita-stampo>
<discesacornice v-if="show==1" :recipe="recipe"></discesacornice>
<controstampo v-if="show==2" :recipe="recipe"></controstampo>
<salita-stampo v-if="show=='mould'" :recipe="recipe"></salita-stampo>
<discesacornice v-if="show=='frame'" :recipe="recipe"></discesacornice>
<controstampo v-if="show=='upperplate'" :recipe="recipe"></controstampo>
<footer>
<button
class="btn"
@@ -32,7 +32,15 @@ export default class Raffreddamento extends Vue {
deferred: Deferred<boolean>;
recipe: Recipe.IRecipe = this.$store.getters.getCurrent();
show: number = 0;
show: string = 'blowing';
@Prop()
value: string;
mounted() {
if (this.value)
this.show = this.value;
}
annulla() {
recipeService.Cancel();
@@ -98,6 +106,10 @@ export default class Raffreddamento extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
@@ -105,7 +117,7 @@ export default class Raffreddamento extends Vue {
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -3,28 +3,28 @@
<modal type="raffreddamento-info">
<div class="tab-header" slot="header-buttons">
<button
:class="{'active':show == 0}"
@click="show=0"
:class="{'active':show == 'blowing'}"
@click="show='blowing'"
v-if="isBlowingEnabled()"
>{{'blowers' | localize("Ventilatori")}}</button>
<button
:class="{'active':show == 1}"
@click="show=1"
:class="{'active':show == 'pyrometer'}"
@click="show='pyrometer'"
v-if="isPyrometerEnabled()"
>{{'pyrometers' | localize("Pirometro")}}</button>
<button
:class="{'active':show == 2}"
@click="show=2"
:class="{'active':show == 'nebulizer'}"
@click="show='nebulizer'"
v-if="isNebulizerEnabled()"
>{{'nebulizers' | localize("Nebulizzatori")}}</button>
<button
:class="{'active':show == 3}"
@click="show=3"
:class="{'active':show == 'telescopic'}"
@click="show='telescopic'"
v-if="isTelescopicEnabled()"
>{{'telescopic' | localize("Telescopi")}}</button>
<button
:class="{'active':show == 4}"
@click="show=4"
:class="{'active':show == 'shutter'}"
@click="show='shutter'"
v-if="isShutterEnabled()"
>{{'shutters' | localize("Otturatori")}}</button>
</div>
@@ -33,11 +33,11 @@
<i class="fa fa-remove"></i>
</button>
</div>
<ventilatori v-if="show==0" :recipe="recipe"></ventilatori>
<pirometro v-if="show==1" :recipe="recipe"></pirometro>
<nebulizzatori v-if="show==2" :recipe="recipe"></nebulizzatori>
<telescopi v-if="show==3" :recipe="recipe"></telescopi>
<otturatori v-if="show==4" :recipe="recipe"></otturatori>
<ventilatori v-if="show=='blowing'" :recipe="recipe"></ventilatori>
<pirometro v-if="show=='pyrometer'" :recipe="recipe"></pirometro>
<nebulizzatori v-if="show=='nebulizer'" :recipe="recipe"></nebulizzatori>
<telescopi v-if="show=='telescopic'" :recipe="recipe"></telescopi>
<otturatori v-if="show=='shutter'" :recipe="recipe"></otturatori>
<footer>
<button
class="btn"
@@ -18,11 +18,19 @@ import { debounce } from "@/_base/debounce";
export default class ShowRiscaldamentoSuperioreInfo extends Vue {
recipe: Recipe.IRecipe = this.$store.getters.getCurrent();
show: number = 0;
show: string = 'upperheaters';
@Prop()
deferred: Deferred<boolean>;
@Prop()
value: string;
mounted() {
if (this.value)
this.show = this.value;
}
annulla(name: string) {
recipeService.Cancel();
// ModalHelper.HideModal();
@@ -72,13 +80,17 @@ export default class ShowRiscaldamentoSuperioreInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -3,18 +3,18 @@
<modal type="riscaldi-info">
<div class="tab-header" slot="header-buttons">
<button
:class="{'active':show == 0}"
@click="show = 0"
:class="{'active':show == 'upperheaters'}"
@click="show = 'upperheaters'"
v-if="isUpperheatersEnabled()"
>{{'upperheaters' | localize('Riscaldi superiori')}}</button>
<button
:class="{'active':show == 1}"
@click="show = 1"
:class="{'active':show == 'lowerheaters'}"
@click="show = 'lowerheaters'"
v-if="isLowerheatersEnabled()"
>{{'lowerheaters' | localize('Riscaldi inferiori')}}</button>
<button
:class="{'active':show == 2}"
@click="show = 2"
:class="{'active':show == 'decomsustain'}"
@click="show = 'decomsustain'"
v-if="isDecomsustainEnabled()"
>{{'decomsustain' | localize("Sostentamento/Decompressione")}}</button>
</div>
@@ -23,10 +23,10 @@
<i class="fa fa-remove"></i>
</button>
</div>
<riscaldiinf v-if="show == 1" :recipe="recipe"></riscaldiinf>
<riscaldisup v-if="show == 0" :recipe="recipe"></riscaldisup>
<sostdecomp v-if="show == 2" :recipe="recipe"></sostdecomp>
<footer>
<riscaldiinf v-if="show == 'lowerheaters'" :recipe="recipe"></riscaldiinf>
<riscaldisup v-if="show == 'upperheaters'" :recipe="recipe"></riscaldisup>
<sostdecomp v-if="show == 'decomsustain'" :recipe="recipe"></sostdecomp>
<footer>
<button
class="btn"
@click="annulla()"
@@ -100,13 +100,17 @@ export default class ShowVuotoInfo extends Vue {
const el = this.payload[key] as Recipe.IValue;
if (el.setpointHMI != el.setpointPLC) result = true;
}
for (const key in store.state.warmers.channels) {
const el = store.state.warmers.channels[key];
if (el.setpointHMI != el.setpointPLC) result = true;
}
return result;
}
hasEnabledFields(...fields: string[]) {
let result = false;
for (const field of fields) {
if ((this.recipe[field] as Recipe.IValue).status.enabled) result = true;
if ((this.recipe[field] as Recipe.IValue).status.visible) result = true;
}
return result;
}
@@ -1,7 +1,11 @@
<template>
<div class="togglebutton big" v-if="status.visible">
<div class="togglebutton big" v-if="!status || status.visible">
<label>
<input type="checkbox" v-model="bindingValue" :disabled="readOnly || !status.enabled" />
<input
type="checkbox"
v-model="bindingValue"
:disabled="readOnly || (status && !status.enabled)"
/>
<span
class="toggle"
:class="{'error': status && status.hasError}"
@@ -17,9 +17,9 @@ Vue.filter("localize", function (key: string, defaultValue: string, ...args): st
if (!value)
dvalue = labels[key];
if (!value && !dvalue) {
console.debug("missing localization key:", key);
}
// if (!value && !dvalue) {
// console.debug("missing localization key:", key);
// }
return pf.vsprintf(value || dvalue || defaultValue, args);
});
@@ -2,6 +2,8 @@ import Vue from "vue";
import Component from "vue-class-component";
import { appModelActions, store, machineStatusActions } from "@/store";
import { prodService } from "@/services/prodService";
import AvvioProduzione from "@/app_modules_thermo/setup/avvio-produzione/avvio-produzione.vue";
import { ModalHelper } from "@/components/modals";
// import VueGesture from "@/_base/gestures.vue";
// import { Factory, messageService } from "src/_base";
@@ -53,15 +55,15 @@ export default class AppFooter extends Vue {
}
public getUtilities(): Array<Object> {
// if (typeof cmsClient != "undefined")
// return JSON.parse(cmsClient.getConfiguredProcessesInMainMenu());
// else
return new Array<Object>();
if (typeof cmsClient != "undefined")
return JSON.parse(cmsClient.getConfiguredProcessesInMainMenu());
else
return new Array<Object>();
}
public startUtility(id: any) {
// if (typeof cmsClient != "undefined")
// cmsClient.openOrStartProcess(id);
if (typeof cmsClient != "undefined")
cmsClient.openOrStartProcess(id);
}
showDashboard() {
@@ -73,6 +75,6 @@ export default class AppFooter extends Vue {
}
async runAuto() {
await prodService.Auto();
await ModalHelper.ShowModalAsync(AvvioProduzione);
}
}
@@ -66,6 +66,23 @@
class="oval clock"
:class="{ big:isInPath('clock') && !state.isMainViewLiftedUp}"
></button>
<div v-if="isAreaVisible('utilities') && getUtilities().length > 0" class="divider">
<i class="fa fa-circle"></i>
</div>
<template v-for="software in getUtilities()">
<button
@click="startUtility(software.id)"
:title="software.longName"
:key="software.id"
:disabled="!isAreaEnabled('utilities')"
v-if="isAreaVisible('utilities')"
class="oval externUtility"
>
<img v-if="software.iconBase64 && software.iconBase64!=''" :src="software.iconBase64" />
<div v-if="!software.iconBase64 || software.iconBase64==''">{{software.shortName}}</div>
</button>
</template>
</div>
<div class="machine-area">
@@ -131,6 +131,7 @@ export default class AppHeader extends Vue {
async saveRecipe() {
await recipeService.Save();
messageService.publishToChannel('print')
}
}
@@ -121,14 +121,14 @@
<label class="text">{{getDate(selectedFile.LastModDate)}}</label>
</div>
</div>
<div class="group-button">
<!-- <div class="group-button">
<button class="btn" v-if="false">
<i class="fa fa-clone"></i>
</button>
<button class="btn" :disabled="!selectedFile.CanEdit" @click="editprogram">
<i class="fa fa-pencil-square-o"></i>
</button>
</div>
</div>-->
</div>
<div class="selected-item-body" v-if="!selectedFile.IsJob" :class="{blur:fileIsChanged}">
@@ -0,0 +1,387 @@
import Vue from "vue";
import Component from "vue-class-component";
import { ModalHelper, Modal } from "@/components/modals";
import { Factory, messageService, awaiter } from "../../_base";
import moment from "moment";
import cardFolderPath from "./cards/card-folder-path.vue";
import * as iziToast from "izitoast";
import ZoomImage from './zoom-image.vue'
import { recipeService } from "@/services/recipeService";
declare var cmsClient: any;
interface PathInfo {
Name: string;
AbsolutePath: string;
Path: string;
IsDirectory: boolean;
IsMain: boolean;
FileExist: boolean;
}
interface FileInfo {
Name: string;
AbsolutePath: string;
CreationDate: string;
LastModDate: string;
Content: Array<any>;
PreviewBase64: any;
CanEdit: boolean;
IsJob: boolean;
}
@Component({
components: {
modal: Modal,
cardFolderPath,
ZoomImage
}
})
export default class ModalSaveProgram extends Vue {
driveList: Array<PathInfo> = [];
currentPath: string = "";
currentDrive: string = null;
lastClickPath: string = "";
breadcrumbs: Array<any> = [];
firstColumnData: Array<PathInfo> = [];
secondColumnData: Array<PathInfo> = [];
isLocalNavigation: boolean = true;
selectedFile: FileInfo = null;
selectedFileMetadata = null;
currentFilterFirst: string = "";
currentFilterSecond: string = "";
navigationDepth: number = 0;
selectedNumberImage: number = 0;
showZoomedImage: boolean = false;
mustCLeanPProgram: boolean = false;
fileIsChanged: boolean = false;
onLabel: string = "On";
offLabel: string = "Off";
recentPath: string = ""
thereAreFileEliminated: boolean = false
askConfirmLayer: boolean = false
loadClicked: boolean = false
recentDriveSelected: boolean = false
get machineInfo() {
return this.$store.state.machineInfo;
}
get canLoadProgram() {
return this.$store.state.process.canLoadProgram;
}
async mounted() {
if (typeof cmsClient != "undefined") {
this.driveList = JSON.parse(cmsClient.getOSdriveList());
this.recentPath = cmsClient.RECENT_FOLDER_KEY
}
this.loadClicked = false
}
async navigateTo(path: string, absolutePath: string, local: boolean, todepth: number, fromdepth: number) {
this.currentPath = absolutePath;
this.lastClickPath = absolutePath;
this.checkChangeNavigationType(local);
this.selectedFile = null;
this.selectedFileMetadata = null;
var files = await this.getFilesForPath(absolutePath, path);
// controlla se impostare il currentDrive
if (fromdepth == 0) this.currentDrive = absolutePath;
// controlla come organizzare le colonne.
if (this.navigationDepth == 2 && todepth == 2 && fromdepth == 2) {
this.fillArray(this.firstColumnData, this.secondColumnData);
}
this.navigationDepth = todepth;
// Check destination depth
if (todepth == 1) {
this.fillArray(this.firstColumnData, files);
this.secondColumnData.splice(0, this.secondColumnData.length);
this.currentFilterFirst = "";
}
if (todepth == 2) {
this.fillArray(this.secondColumnData, files);
this.currentFilterSecond = "";
}
// Recent column
if (path == this.recentPath) {
var main = null;
// Parse files
files.forEach((element) => {
// Check if there is a deleted file
if (element.FileExist == false)
this.thereAreFileEliminated = true
// Return only the main program
if (element.IsMain == true)
main = element
})
// If there is a main program
if (main != null) {
this.fileInfo(main.AbsolutePath, false, 1)
this.recentDriveSelected = true;
}
}
else {
this.thereAreFileEliminated = false;
this.recentDriveSelected = false;
}
this.calcBreadCrumb(absolutePath);
this.cleanFileWatcher();
}
fillArray(destinationArray: Array<any>, sourceArray: Array<any>) {
destinationArray.splice(0, destinationArray.length);
for (const key in sourceArray) {
if (sourceArray.hasOwnProperty(key)) {
const element = sourceArray[key];
destinationArray.push(element);
}
}
}
checkChangeNavigationType(local: boolean) {
if (this.isLocalNavigation != local) {
this.navigationDepth = 0;
this.firstColumnData.splice(0, this.firstColumnData.length);
this.secondColumnData.splice(0, this.secondColumnData.length);
}
}
async getFilesForPath(absolutePath: string, path: string,): Promise<Array<any>> {
var result = null;
result = JSON.parse(cmsClient.getFileList(absolutePath));
return this.toUpperCaseModel(result).sort(this.compareDirectoriesFirst);
}
private compareDirectoriesFirst(x, y) {
return (x.IsDirectory === y.IsDirectory) ? 0 : x.IsDirectory ? -1 : 1;
}
toUpperCaseModel(data: Array<any>): Array<PathInfo> {
return data.map(i => {
return {
Name: i.name || i.Name,
AbsolutePath: i.absolutePath || i.AbsolutePath,
Path: i.path || i.Path,
IsDirectory: (i.isDirectory != null && i.isDirectory) || (i.IsDirectory != null && i.IsDirectory),
IsJob: i.isJob,
IsMain: i.IsMain,
FileExist: i.FileExist
};
});
}
calcBreadCrumb(path: string) {
this.breadcrumbs.splice(0, this.breadcrumbs.length);
if (path.startsWith("\\\\")) {
this.breadcrumbs.push({
name: "CN",
path: "\\\\"
});
}
if (path) {
var fragments = path.split("\\");
var __p = "";
fragments.forEach(element => {
__p = __p + element + "\\";
this.breadcrumbs.push({
name: element,
path: __p
});
});
}
}
isInPath(path, isDirectory) {
if (this.currentPath) {
return (path.startsWith(this.currentPath) && !isDirectory) || (path == this.currentPath && isDirectory);
}
return false;
}
toUpperCaseFileModel(data: any): FileInfo {
return {
AbsolutePath: data.absolutePath || data.AbsolutePath,
Path: data.path || data.Path,
Content: data.content || data.Content,
CreationDate: data.creationDate || data.CreationDate,
LastModDate: data.lastModDate || data.LastModDate,
Name: data.name || data.Name,
PreviewBase64: data.previewBase64 || data.PreviewBase64,
IsJob: data.isJob || data.IsJob,
CanEdit: data.canEdit || data.CanEdit
} as FileInfo;
}
async fileInfo(str, isJob, fromdepth: number) {
this.lastClickPath = str;
if (fromdepth == 1)
this.secondColumnData.splice(0, this.secondColumnData.length);
if (this.isLocalNavigation && typeof cmsClient != "undefined") {
// Select file
this.selectedFile = this.toUpperCaseFileModel(
JSON.parse(cmsClient.getProgramInfo(str))
);
// Check if the file it's a job and get data
this.selectedFile.IsJob = isJob;
if (isJob) {
var resp = JSON.parse(cmsClient.readJobMetadata(str));
if (resp.error) {
this.selectedFile = null;
(iziToast as any).error({
title: "error",
message: resp.error,
theme: "dark",
timeout: 10000,
class: "t-error",
transitionOut: "fadeOut",
})
return
}
this.selectedFileMetadata = resp.metadata;
this.selectedNumberImage = 0;
}
}
this.currentPath = this.selectedFile.AbsolutePath.substring(0, this.selectedFile.AbsolutePath.lastIndexOf("\\"));
this.cleanFileWatcher();
}
beforeMount() {
messageService.subscribeToChannel("esc_pressed", args => {
this.close();
});
}
beforeDestroy() {
messageService.deleteChannel("esc_pressed");
}
close() {
try {
this.cleanFileWatcher()
messageService.deleteChannel("esc_pressed");
} catch{ }
// ModalHelper.HideModal();
this.$router.back();
}
reload() {
this.driveList = JSON.parse(cmsClient.getOSdriveList());
}
getDate(date) {
return moment(date).format("L");
}
async load(item: FileInfo) {
if (!this.thereAreFileEliminated || this.askConfirmLayer) {
this.loadClicked = true
await this.loadProgram(item.AbsolutePath);
this.$router.push({ name: 'setup' })
this.askConfirmLayer = false
}
else {
this.askConfirmLayer = true
}
this.loadClicked = false
}
async loadProgram(path: string) {
if (typeof cmsClient != "undefined") {
try {
await recipeService.Load(path);
this.close();
} catch (err) {
(iziToast as any).error({
title: err.split(";")[0],
message: err.split(";")[1],
theme: "dark",
timeout: 10000,
class: "t-error",
transitionOut: "fadeOut",
})
}
}
}
selectNumberImage(value) {
this.selectedNumberImage = value;
}
async loadJob(path: string) {
var jobMetadata = cmsClient.readJobMetadata(path);
}
ToggleShowZoomedImage() {
this.showZoomedImage = !this.showZoomedImage;
}
editprogram() {
var res = cmsClient.editProgram(this.selectedFile.AbsolutePath, this.fileChanged);
if (res) {
var obj = JSON.parse(res);
if (obj.error) {
(iziToast as any).error({
title: "error",
message: obj.error,
theme: "dark",
timeout: 10000,
class: "t-error",
transitionOut: "fadeOut",
})
}
}
else {
this.fileIsChanged = false;
}
}
fileChanged() {
this.fileIsChanged = true;
}
reloadProgram() {
this.fileInfo(this.selectedFile.AbsolutePath, this.selectedFile.IsJob, this.navigationDepth)
this.fileIsChanged = false;
}
cleanFileWatcher() {
this.fileIsChanged = false;
cmsClient.cleanFileWatcher()
}
askConfirmOkClick() {
this.load(this.selectedFile)
}
askConfirmCancelClick() {
this.askConfirmLayer = false;
}
}
@@ -0,0 +1,230 @@
<template>
<modal
type="modal-load-program"
:title="'modal_load_program_lbl_title_window' | localize('Selezione programma')"
>
<button class="close" slot="header-buttons" @click="close()">
<i class="fa fa-remove"></i>
</button>
<div class="modal-load-program-header">
<!-- <div class="title">
<i class="fa fa-chevron-left"></i>
Selezione programma da aggiungere in coda
</div>-->
<div class="box-search">
<div class="path" v-if="!showZoomedImage">
<div class="child" v-for="(bread,key) in this.breadcrumbs" :key="key">
<div v-if="bread.name && key!=0">
<i class="fa fa-chevron-right"></i>
</div>
<a href="#" @click="navigateTo(bread.path,bread.path,isLocalNavigation, 1,1)">{{bread.name}}</a>
</div>
<!-- <div class="root">PC</div>
<div v-if="true"><i class="fa fa-chevron-right"></i></div>
<div class="child">Cliente A</div>
<div v-if="true"><i class="fa fa-chevron-right"></i></div>
<div class="child">Part Program 01</div>-->
</div>
<div class="path" v-if="showZoomedImage">{{selectedFile.AbsolutePath}}</div>
</div>
</div>
<div class="modal-load-program-body">
<div class="first-column" :class="{blur:mustCLeanPProgram}">
<!-- <card-folder-path
name="CNC"
:iconType="'DATABASE'"
:selected="'\\\\' == currentDrive"
:disabled="!isCnReady()"
@click="navigateTo('\\\\','\\\\', false, 1,0)"
></card-folder-path>-->
<!-- <hr /> -->
<card-folder-path
name="Recent"
:iconType="'SPFO'"
:selected="currentDrive == recentPath"
@click="navigateTo(recentPath, recentPath, true, 1, 0)"
></card-folder-path>
<hr />
<card-folder-path
v-for="(val, key) in this.driveList"
:key="key"
:name="val.Name"
:iconType="val.Type"
:selected="val.Path == currentDrive"
@click="navigateTo(val.Path, val.Path, true, 1,0)"
></card-folder-path>
</div>
<div class="second-column" :class="{blur:mustCLeanPProgram}" v-if="navigationDepth <1"></div>
<div class="second-column" :class="{blur:mustCLeanPProgram}" v-if="navigationDepth >=1">
<div class="group-btn">
<input
type="text"
v-model="currentFilterFirst"
:placeholder="'modal_load_program_search_placeholder' | localize('Cerca un programma per nome')"
/>
<i class="fa fa-2x fa-search"></i>
</div>
<div class="content scrollable">
<card-folder-path
v-for="(val,key) in this.firstColumnData.filter(t => t.Name.toLowerCase().indexOf(currentFilterFirst.toLowerCase()) >=0)"
:key="key"
:name="val.Name"
:disabled="(!isCnReady() && !isLocalNavigation) || !val.FileExist"
:class="{dark: val.AbsolutePath == lastClickPath}"
:selected="isInPath(val.AbsolutePath,val.IsDirectory) || val.AbsolutePath == lastClickPath"
:withArrow="val.IsDirectory == true"
:iconType="val.IsDirectory? 'FOLDER':'FILE'"
:fileExist="val.FileExist"
@click="val.IsDirectory ? navigateTo(val.Path, val.AbsolutePath, isLocalNavigation, 2,1): fileInfo(val.AbsolutePath, val.IsJob, 1)"
></card-folder-path>
</div>
</div>
<div class="third-column" :class="{blur:mustCLeanPProgram}" v-if="navigationDepth <2"></div>
<div class="third-column" :class="{blur:mustCLeanPProgram}" v-if="navigationDepth >=2">
<!-- !(this.arrayViewList[1] ? this.arrayViewList[1].length < 1 : false) -->
<div class="group-btn">
<input
type="text"
v-model="currentFilterSecond"
:placeholder="'modal_load_program_search_placeholder' | localize('Cerca un programma per nome')"
/>
<i class="fa fa-2x fa-search"></i>
</div>
<div class="label-folder-empty" v-if="secondColumnData.length ==0">
<label>{{'modal_load_program_empty_folder' | localize("Cartella vuota")}}</label>
</div>
<div class="content scrollable" v-if="secondColumnData.length >0">
<card-folder-path
v-for="val in (this.secondColumnData.filter(t => t.Name.toLowerCase().indexOf(currentFilterSecond.toLowerCase()) >=0))"
:key="val.Name"
:name="val.Name"
:disabled="!isCnReady() && !isLocalNavigation"
:class="{dark: val.AbsolutePath == lastClickPath}"
:selected="isInPath(val.AbsolutePath,val.IsDirectory) || val.AbsolutePath == lastClickPath"
:withArrow="val.IsDirectory == true"
:iconType="val.IsDirectory? 'FOLDER':'FILE'"
@click="val.IsDirectory? navigateTo(val.Path, val.AbsolutePath, isLocalNavigation, 2, 2) : fileInfo(val.AbsolutePath, val.IsJob, 2) "
></card-folder-path>
<!-- <card-folder-path name="Cliente A" @click="selectItem()" :with-arrow="false"></card-folder-path>-->
</div>
</div>
<div class="selected-item" :class="{blur: fileIsChanged}" v-if="selectedFile">
<div class="selected-item-header" :class="{blur: fileIsChanged}">
<div>
<label class="selected-item-title">{{selectedFile.Name}}</label>
<div class="subtitle">
<label class="title">{{'modal_load_program_creation_date' | localize("Creation Date:")}}</label>
<label class="text">{{getDate(selectedFile.CreationDate)}}</label>
<label
class="title"
>{{'modal_load_program_last_change_date' | localize("Last Change Date:")}}</label>
<label class="text">{{getDate(selectedFile.LastModDate)}}</label>
</div>
</div>
<!-- <div class="group-button">
<button class="btn" v-if="false">
<i class="fa fa-clone"></i>
</button>
<button class="btn" :disabled="!selectedFile.CanEdit" @click="editprogram">
<i class="fa fa-pencil-square-o"></i>
</button>
</div>-->
</div>
<div class="selected-item-body" v-if="!selectedFile.IsJob" :class="{blur:fileIsChanged}">
<div class="selected-item-body-image">
<div v-if="selectedFile.PreviewBase64" class="container" @click="ToggleShowZoomedImage">
<img :src="selectedFile.PreviewBase64" />
<div class="eye">
<i class="fa fa-eye" aria-hidden="true"></i>
</div>
</div>
<div
v-else
class="noimage"
>{{'modal_load_program_lbl_img_not_found' | localize('Preview non disponibile')}}</div>
</div>
<div class="selected-item-body-description scrollable">
<div class="row" v-for="(item,index) in selectedFile.Content" :key="'line' + index">
<label>{{item}}</label>
</div>
</div>
</div>
<div
class="selected-item-body"
v-if="selectedFile && selectedFile.IsJob"
:class="{blur:fileIsChanged}"
>
<div class="box-image" v-if="selectedFileMetadata.generics.images.length > 0">
<div class="box-image-container">
<img :src="selectedFileMetadata.generics.images[selectedNumberImage].base64" />
</div>
<div class="linenumber">
<div v-for="(n,i) in selectedFileMetadata.generics.images" :key="i">
<button :class="{'target': i == selectedNumberImage}" @click="selectNumberImage(i)">{{i+1}}</button>
</div>
</div>
</div>
<div v-else class="selected-item-body-image">
<div
class="noimage"
>{{'modal_load_program_lbl_img_not_found' | localize('Preview non disponibile')}}</div>
</div>
<div
class="box-description"
v-if="selectedFileMetadata.generics.description"
>{{selectedFileMetadata.generics.description}}</div>
</div>
</div>
<div
:class="{'selected-item-blur': mustCLeanPProgram}"
class="selected-item"
v-if="!selectedFile"
>
<div class="notselecteditem">
<label>{{'modal_load_program_lbl_box_select_program' | localize('Seleziona programma')}}</label>
</div>
</div>
<zoom-image
v-if="showZoomedImage && selectedFile && selectedFile.PreviewBase64"
:class="{blur:mustCLeanPProgram}"
@close="ToggleShowZoomedImage"
:imageSrc="selectedFile.PreviewBase64"
/>
<div class="fileIsChanged" v-if="fileIsChanged">
{{'modal_load_program_lbl_file_is_changed' | localize('Il file è stato modificato')}}
<div>
<button
class="btn"
@click="reloadProgram()"
>{{'modal_load_program_btn_reload_file' | localize('Ricarica')}}</button>
</div>
</div>
<div class="ask-confirm" v-if="askConfirmLayer">
{{'modal_load_program_lbl_ask_confirm' | localize('Ci sono dei file che sono stati spostati o eliminati, caricare comunque?')}}
<div>
<button
class="btn"
@click="askConfirmCancelClick()"
>{{'userinfo_btn_cancel' | localize('Annulla')}}</button>
<button
class="btn"
@click="askConfirmOkClick()"
>{{'modal_load_program_btn_upload_program' | localize('Carica e attiva programma')}}</button>
</div>
</div>
</div>
<div class="modal-load-program-footer">
<button
class="btn btn-success"
:disabled="!selectedFile || fileIsChanged || loadClicked"
v-if="this.isLocalNavigation"
@click="load(selectedFile)"
>{{'modal_load_program_btn_upload_program' | localize('Carica e attiva programma')}}</button>
</div>
</modal>
</template>
<script src="./modal-save-program.ts" lang="ts"></script>
@@ -33,8 +33,8 @@ export class ProdService extends baseRestService {
await this.Put<any>((await this.BASE_URL()) + "mode/auto", null);
}
async Start(qty: number, newWorkOrder: boolean = true) {
await this.Put<any>((await this.BASE_URL()) + `start?requestQty=${qty}&newWorkOrder=${newWorkOrder}`, null);
async Start(qty: number, newWorkOrder: boolean, riscqty: number) {
await this.Put<any>((await this.BASE_URL()) + `start?requestQty=${qty}&newWorkOrder=${newWorkOrder}&numCicliRisc=${riscqty}`, null);
}
}
export const prodService = new ProdService();
@@ -1,7 +1,7 @@
import { baseRestService } from "../_base/baseRestService";
import { CONFIGURATION } from "@/config";
import { recipeActions } from "@/store/recipe.store";
import { store } from "@/store";
import { store, warmersActions } from "@/store";
import { dataService } from "./dataService";
export class RecipeService extends baseRestService {
@@ -20,6 +20,10 @@ export class RecipeService extends baseRestService {
return result;
}
async GetNote(): Promise<string> {
return this.Get((await this.BASE_URL()) + "recipeNotes");
}
async Update(model: { [id: string]: Recipe.IValue }) {
let payload = {}
@@ -60,6 +64,8 @@ export class RecipeService extends baseRestService {
}
async Cancel() {
recipeActions.reset(store);
warmersActions.reset(store);
let result = await this.Put<any>((await this.BASE_URL()) + "cancel", null, true);
return result;
}
@@ -91,5 +97,17 @@ export class RecipeService extends baseRestService {
return result;
}
async UploadImage(image: Blob, filename: string) {
const formData = new FormData();
formData.append('file', image, filename)
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
let result = await this.Post<any>((await this.BASE_URL()) + `uploadImage`, formData, true);
}
}
export const recipeService = new RecipeService();
@@ -44,6 +44,7 @@ export interface SoftKeysConfigurationModel {
category: number,
operatorConfirmationNeeded: boolean,
type: 2,
refCallParam: string,
subKeys: Object
}
@@ -8,6 +8,7 @@ export interface RecipeStoreModel {
export interface RecipeActions {
reset(context);
setCurrent(context, model: Recipe.IRecipe, avoidSetpointHMI: boolean);
setOverview(context, model: Overview.IOverview);
}
@@ -35,6 +36,7 @@ export const recipeStore = {
upperPlate: "",
vacuum: ""
},
canUpdateRecipe: true,
_map: new Map<number, Recipe.IValue>()
} as RecipeStoreModel,
@@ -45,7 +47,13 @@ export const recipeStore = {
},
mutations: {
Reset(state) {
for (const key in state.current)
if (state.current.hasOwnProperty(key)) {
let m = state.current[key] as Recipe.IValue;
m.setpointHMI = m.setpointPLC;
}
},
SetCurrent(state, model: { avoidSetpoint: boolean, recipe: Recipe.IRecipe }) {
for (const key in model.recipe) {
if (model.recipe.hasOwnProperty(key)) {
@@ -86,7 +94,9 @@ export const recipeStore = {
},
actions: {
reset(context) {
context.commit("Reset")
},
async setCurrent(context, model: Recipe.IRecipe, avoidSetpointHMI: boolean = false) {
context.commit("SetCurrent", { avoidSetpoint: avoidSetpointHMI, recipe: model });

Some files were not shown because too many files have changed in this diff Show More