@@ -15,8 +13,8 @@
}
else
{
-
-
+
+
@RecMSE.IdxMacchina
@@ -30,6 +28,62 @@ else
@code {
[Parameter]
- public MappaStatoExpl? RecMSE { get; set; }
+ public MappaStatoExpl? RecMSE { get; set; } = null;
+
+ [Parameter]
+ public bool DoBlink { get; set; } = false;
+
+ [Parameter]
+ public bool DoAnimate { get; set; } = false;
+
+
+ [Parameter]
+ public int keepAliveMin { get; set; } = 5;
+ protected int kaFactor = 60 / 2;
+
+ protected override async Task OnParametersSetAsync()
+ {
+ Console.WriteLine($"MachineBlock | Parameter SET! | Macc: {RecMSE?.IdxMacchina}");
+ await InvokeAsync(StateHasChanged);
+ }
+
+ private string cssComStatus(string semaforo, DateTime? lastUpdateN)
+ {
+ DateTime lastUpdate = lastUpdateN.HasValue ? (DateTime)lastUpdateN : DateTime.Now.AddHours(-1);
+ string answ = cssStatus(semaforo);
+ if (DateTime.Now.Subtract(lastUpdate).TotalSeconds > (keepAliveMin * kaFactor))
+ {
+ answ = $"{baseCss}Ro";
+ // blink se secondo pari...
+ DateTime adesso = DateTime.Now;
+ int resto = 0;
+ Math.DivRem(adesso.Second, 2, out resto);
+ if (resto == 0)
+ {
+ answ += "_b";
+ }
+ }
+ return answ;
+ }
+
+ private string cssStatus(string codSemaforo)
+ {
+ // se vuoto --> mostra nero!
+ if (string.IsNullOrEmpty(codSemaforo))
+ {
+ codSemaforo = "sNe";
+ }
+ string codColore = codSemaforo.Substring(1, 2);
+ string answ = $"{baseCss}{codColore}";
+ if (DoAnimate && codColore != "Ve")
+ {
+ if (DoBlink)
+ {
+ answ += "_b";
+ }
+ }
+ return answ;
+ }
+ protected string baseCss = "sem";
}
diff --git a/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.css b/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.css
index da778a63..afa3eb83 100644
--- a/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.css
+++ b/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.css
@@ -1,4 +1,5 @@
-.statusMap {
+/* Gestione semafori */
+.statusMap {
/*background: rgba(0,0,0,0.3);*/
}
.statusMap .ui-title,
diff --git a/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.less b/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.less
index a19d4a4c..3f11de19 100644
--- a/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.less
+++ b/MP-TAB/MP-TAB.Client/Components/MachineBlock.razor.less
@@ -1,4 +1,6 @@
+/* Gestione semafori */
+
.statusMap {
/*background: rgba(0,0,0,0.3);*/
}
diff --git a/MP-TAB/MP-TAB.Client/Pages/StatusMap.razor b/MP-TAB/MP-TAB.Client/Pages/StatusMap.razor
index 2440ab01..706c2eff 100644
--- a/MP-TAB/MP-TAB.Client/Pages/StatusMap.razor
+++ b/MP-TAB/MP-TAB.Client/Pages/StatusMap.razor
@@ -1,9 +1,16 @@
@attribute [RenderModeAuto]
+@using MP.Data.Services;
+@using MP.Data;
+@using NLog;
+@using Newtonsoft.Json;
+@inject MonDataFeeder MMDataService
+@implements IDisposable
+
- @if (ListMSE == null || ListMSE.Count == 0)
+ @if (listMSE == null || listMSE.Count == 0)
{
- @for (int i = 0; i < 9; i++)
+ @for (int i = 0; i < 10; i++)
{
@@ -12,10 +19,10 @@
}
else
{
- @foreach (var item in ListMSE)
+ @foreach (var item in listMSE)
{
-
+
}
}
@@ -23,6 +30,93 @@
@code {
- [Parameter]
- public List
? ListMSE { get; set; }
+ private List? listMSE { get; set; } = null;
+ private bool doBlink = false;
+
+ public void Dispose()
+ {
+ DateTime adesso = DateTime.Now;
+ if (adesso.Subtract(lastStart).TotalMilliseconds < 800)
+ {
+ Log.Debug($"StatusMap | SALTATO dispose");
+ }
+ else
+ {
+
+ MMDataService.dataPipe.EA_NewMessage -= DataPipe_EA_NewMessage;
+ MMDataService.blinkPipe.EA_NewMessage -= BlinkPipe_EA_NewMessage;
+ MMDataService.stopTimers();
+ // disposeTimers();
+ Log.Debug($"StatusMap | richiamato dispose");
+ }
+ }
+
+ protected override async Task OnInitializedAsync()
+ {
+ // MMDataService.dataPipe.EA_NewMessage -= DataPipe_EA_NewMessage;
+ // MMDataService.blinkPipe.EA_NewMessage -= BlinkPipe_EA_NewMessage;
+ MMDataService.dataPipe.EA_NewMessage += DataPipe_EA_NewMessage;
+ MMDataService.blinkPipe.EA_NewMessage += BlinkPipe_EA_NewMessage;
+ MMDataService.startTimers();
+ lastStart = DateTime.Now;
+ await Task.Delay(1);
+ Log.Debug($"StatusMap | OnInitializedAsync");
+ // return base.OnInitializedAsync();
+ }
+
+ private DateTime lastStart = DateTime.Now;
+
+
+ private void BlinkPipe_EA_NewMessage(object? sender, EventArgs e)
+ {
+ PubSubEventArgs currArgs = (PubSubEventArgs)e;
+ Log.Trace($"BlinkPipe_EA_NewMessage | messsage received!");
+ // conversione on-the-fly List --> allarmi
+ if (!string.IsNullOrEmpty(currArgs.newMessage))
+ {
+ try
+ {
+ var dataRaw = JsonConvert.DeserializeObject(currArgs.newMessage);
+ if (dataRaw != null)
+ {
+ bool.TryParse($"{dataRaw}", out doBlink);
+ }
+ }
+ catch (Exception exc)
+ {
+ Log.Error($"Exception on BlinkPipe_EA_NewMessage{Environment.NewLine}{exc}");
+ }
+ }
+ }
+ private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
+
+ ///
+ /// Ricevuto nuovi dati da mostrare!
+ ///
+ ///
+ ///
+ ///
+ private void DataPipe_EA_NewMessage(object? sender, EventArgs e)
+ {
+ PubSubEventArgs currArgs = (PubSubEventArgs)e;
+ Log.Trace($"DataPipe_EA_NewMessage | messsage received!");
+ // conversione on-the-fly List --> allarmi
+ if (!string.IsNullOrEmpty(currArgs.newMessage))
+ {
+ listMSE = null;
+ try
+ {
+ var dataList = JsonConvert.DeserializeObject>(currArgs.newMessage);
+ if (dataList != null)
+ {
+ listMSE = dataList;
+ InvokeAsync(StateHasChanged);
+ }
+ }
+ catch (Exception exc)
+ {
+ Log.Error($"Exception on DataPipe_EA_NewMessage{Environment.NewLine}{exc}");
+ }
+ }
+ }
}
diff --git a/MP-TAB/MP-TAB/Components/App.razor b/MP-TAB/MP-TAB/Components/App.razor
index 9439c814..6065d971 100644
--- a/MP-TAB/MP-TAB/Components/App.razor
+++ b/MP-TAB/MP-TAB/Components/App.razor
@@ -8,7 +8,7 @@
@* *@
-
+
diff --git a/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor b/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor
index 60cb1979..1b677ac9 100644
--- a/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor
+++ b/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor
@@ -1,8 +1,8 @@
@page "/status-map"
-@attribute [StreamRendering(true)]
+
+@* @attribute [StreamRendering(true)] *@
-
-
+
diff --git a/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor.cs b/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor.cs
index 4dcbde73..724d98a9 100644
--- a/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor.cs
+++ b/MP-TAB/MP-TAB/Components/Pages/StatusMap.razor.cs
@@ -20,22 +20,27 @@ using MP_TAB.Components;
using NLog;
using MP.Data.Conf;
using Newtonsoft.Json;
+using static System.Runtime.InteropServices.JavaScript.JSType;
namespace MP_TAB.Components.Pages
{
- public partial class StatusMap : IDisposable
+ public partial class StatusMap
{
- [Inject]
- protected MonDataFeeder MMDataService { get; set; } = null!;
- [Inject]
- protected NavigationManager NavManager { get; set; } = null!;
+ #region Protected Fields
protected bool doAnimate = true;
- protected int fastRefreshMs = 1000;
protected int keepAliveMin = 1;
protected int maxCol = 6;
protected string showArt = "";
protected int slowRefreshSec = 300;
+
+ #endregion Protected Fields
+
+ #region Protected Properties
+
+ [Inject]
+ protected MonDataFeeder MMDataService { get; set; } = null!;
+
protected int slowRefreshMs
{
get
@@ -46,105 +51,9 @@ namespace MP_TAB.Components.Pages
}
}
- public void Dispose()
- {
- disposeTimers();
- }
- public async void ElapsedSlowTimer(object? source, System.Timers.ElapsedEventArgs e)
- {
- listMSE = null;
- await Task.Delay(10);
- Log.Info("Elapsed Slow Timer --> full page reload");
- // dispongo i vari timers...
- disposeTimers();
- await Task.Delay(10);
- // reload pagina
- NavManager.NavigateTo(NavManager.Uri);
- }
+ #endregion Protected Properties
- public void StartTimer()
- {
- // timer lento
- slowTimer = new System.Timers.Timer(slowRefreshMs);
- slowTimer.Elapsed += ElapsedSlowTimer;
- slowTimer.Enabled = true;
- slowTimer.Start();
- }
-
-
- protected override async Task OnInitializedAsync()
- {
- await setupConf();
- MMDataService.dataPipe.EA_NewMessage += DataPipe_EA_NewMessage;
- MMDataService.blinkPipe.EA_NewMessage += BlinkPipe_EA_NewMessage;
- Random rnd = new Random();
- await Task.Delay(rnd.Next(100, 200));
- // carico i dati iniziali...
- listMSE = await MMDataService.MseGetAll();
- StartTimer();
- }
-
- private void BlinkPipe_EA_NewMessage(object? sender, EventArgs e)
- {
- PubSubEventArgs currArgs = (PubSubEventArgs)e;
- // conversione on-the-fly List --> allarmi
- if (!string.IsNullOrEmpty(currArgs.newMessage))
- {
- try
- {
- var dataRaw = JsonConvert.DeserializeObject(currArgs.newMessage);
- if (dataRaw != null)
- {
- bool.TryParse($"{dataRaw}", out doBlink);
- }
- }
- catch
- { }
- InvokeAsync(() =>
- {
- StateHasChanged();
- });
- }
- }
-
- ///
- /// Ricevuto nuovi dati da mostrare!
- ///
- ///
- ///
- ///
- private void DataPipe_EA_NewMessage(object? sender, EventArgs e)
- {
- PubSubEventArgs currArgs = (PubSubEventArgs)e;
- // conversione on-the-fly List --> allarmi
- if (!string.IsNullOrEmpty(currArgs.newMessage))
- {
- try
- {
- var dataList = JsonConvert.DeserializeObject>(currArgs.newMessage);
- if (dataList != null)
- {
- listMSE = dataList;
- }
- }
- catch
- { }
- }
- InvokeAsync(StateHasChanged);
- }
-
- private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
- private static System.Timers.Timer slowTimer = new System.Timers.Timer(300000);
- private List? CurrConfig = null;
- private bool doBlink = false;
- private static List? listMSE = null;
- private Random rnd = new Random();
- private void disposeTimers()
- {
- slowTimer.Elapsed -= ElapsedSlowTimer;
- slowTimer.Stop();
- slowTimer.Dispose();
- }
+ #region Protected Methods
///
/// Recupera il valore e se trovato aggiorna
@@ -167,6 +76,7 @@ namespace MP_TAB.Components.Pages
}
return answ;
}
+
///
/// Recupera il valore e se trovato aggiorna
///
@@ -188,41 +98,6 @@ namespace MP_TAB.Components.Pages
return answ;
}
- private async Task setupConf()
- {
- CurrConfig = await MMDataService.ConfigGetAll();
- if (CurrConfig != null && CurrConfig.Count > 0)
- {
- // sistemo i parametri opzionali...
- getConfValInt("keepAliveMin", ref keepAliveMin);
- getConfValInt("MON_maxCol", ref maxCol);
- int intDoAnim = 0;
- getConfValInt("doAnimate", ref intDoAnim);
- doAnimate = intDoAnim == 1;
- getConfValInt("pageRefreshSec", ref slowRefreshSec);
- getConfVal("sART", ref showArt);
- Log.Info($"setupConf | Effettuato setup parametri | keepAlive: {keepAliveMin} | MaxCol: {maxCol} | doAnimate: {doAnimate} | slowRefreshSec: {slowRefreshSec} | fastRefreshMs: {fastRefreshMs}");
- }
- }
- ///
- /// Recupera da redis (in una chiamata soltanto) tutti i valori richiesti e compone un
- /// dizionario x ottimizzare visualizzazione
- ///
- ///
- ///
- protected Dictionary getTagVal(string codIob)
- {
- Dictionary answ = new Dictionary();
- // recupero conf tags...
- var currTags = getIobTag(codIob);
- if (currTags != null && currTags.Count > 0)
- {
- // FIXME TODO !!!! FARE !!!! - da verificare
- answ = currTags.ToDictionary(x => x.TagLocation, x => MMDataService.getTagConf(x.TagLocation));
- }
- return answ;
- }
-
///
/// Recupera da conf eventuale setup tag dell'IOB indicato
///
@@ -241,5 +116,61 @@ namespace MP_TAB.Components.Pages
}
return answ;
}
+
+ ///
+ /// Recupera da redis (in una chiamata soltanto) tutti i valori richiesti e compone un
+ /// dizionario x ottimizzare visualizzazione
+ ///
+ ///
+ ///
+ protected Dictionary getTagVal(string codIob)
+ {
+ Dictionary answ = new Dictionary();
+ // recupero conf tags...
+ var currTags = getIobTag(codIob);
+ if (currTags != null && currTags.Count > 0)
+ {
+ // FIXME TODO !!!! FARE !!!! - da verificare
+ answ = currTags.ToDictionary(x => x.TagLocation, x => MMDataService.getTagConf(x.TagLocation));
+ }
+ return answ;
+ }
+
+ protected override async Task OnInitializedAsync()
+ {
+ await setupConf();
+ }
+
+ #endregion Protected Methods
+
+ #region Private Fields
+
+ private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
+ private static System.Timers.Timer slowTimer = new System.Timers.Timer(300000);
+ private List? CurrConfig = null;
+ private Random rnd = new Random();
+
+ #endregion Private Fields
+
+ #region Private Methods
+
+ private async Task setupConf()
+ {
+ CurrConfig = await MMDataService.ConfigGetAll();
+ if (CurrConfig != null && CurrConfig.Count > 0)
+ {
+ // sistemo i parametri opzionali...
+ getConfValInt("keepAliveMin", ref keepAliveMin);
+ getConfValInt("MON_maxCol", ref maxCol);
+ int intDoAnim = 0;
+ getConfValInt("doAnimate", ref intDoAnim);
+ doAnimate = intDoAnim == 1;
+ getConfValInt("pageRefreshSec", ref slowRefreshSec);
+ getConfVal("sART", ref showArt);
+ Log.Info($"setupConf | Effettuato setup parametri | keepAlive: {keepAliveMin} | MaxCol: {maxCol} | doAnimate: {doAnimate} | slowRefreshSec: {slowRefreshSec}");
+ }
+ }
+
+ #endregion Private Methods
}
}
\ No newline at end of file
diff --git a/MP-TAB/MP-TAB/MP-TAB.csproj b/MP-TAB/MP-TAB/MP-TAB.csproj
index 2993cd68..a659dacd 100644
--- a/MP-TAB/MP-TAB/MP-TAB.csproj
+++ b/MP-TAB/MP-TAB/MP-TAB.csproj
@@ -1,4 +1,4 @@
-
+
net8.0
@@ -8,6 +8,13 @@
$(AssemblyName.Replace(' ', '_'))
+
+
+
+
+
+
+
diff --git a/MP-TAB/MP-TAB/Program.cs b/MP-TAB/MP-TAB/Program.cs
index 50a23896..35048c4e 100644
--- a/MP-TAB/MP-TAB/Program.cs
+++ b/MP-TAB/MP-TAB/Program.cs
@@ -5,14 +5,15 @@ using StackExchange.Redis;
var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
// REDIS setup
-string connStringRedis = configuration.GetConnectionString("Redis");
-string redisSrvAddr = connStringRedis.Substring(0, connStringRedis.IndexOf(":"));
+var cString = configuration.GetConnectionString("Redis");
+string connStringRedis = cString ?? "localhost:6379, DefaultDatabase=1, connectTimeout=5000, syncTimeout=5000, asyncTimeout=5000, abortConnect=false, ssl=false";
+//string redisSrvAddr = connStringRedis.Substring(0, connStringRedis.IndexOf(":"));
// avvio oggetto shared x redis...
var redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis);
// Add services to the container.
builder.Services.AddSingleton(redisMultiplexer);
-builder.Services.AddSingleton();
+builder.Services.AddSingleton();
// nuova versione contenuti...
builder.Services.AddRazorComponents()
diff --git a/MP-TAB/MP-TAB/compilerconfig.json b/MP-TAB/MP-TAB/compilerconfig.json
new file mode 100644
index 00000000..8ec7a4e4
--- /dev/null
+++ b/MP-TAB/MP-TAB/compilerconfig.json
@@ -0,0 +1,6 @@
+[
+ {
+ "outputFile": "wwwroot/app.css",
+ "inputFile": "wwwroot/app.less"
+ }
+]
\ No newline at end of file
diff --git a/MP-TAB/MP-TAB/compilerconfig.json.defaults b/MP-TAB/MP-TAB/compilerconfig.json.defaults
new file mode 100644
index 00000000..41870cc5
--- /dev/null
+++ b/MP-TAB/MP-TAB/compilerconfig.json.defaults
@@ -0,0 +1,71 @@
+{
+ "compilers": {
+ "less": {
+ "autoPrefix": "",
+ "cssComb": "none",
+ "ieCompat": true,
+ "math": null,
+ "strictMath": false,
+ "strictUnits": false,
+ "relativeUrls": true,
+ "rootPath": "",
+ "sourceMapRoot": "",
+ "sourceMapBasePath": "",
+ "sourceMap": false
+ },
+ "sass": {
+ "autoPrefix": "",
+ "loadPaths": "",
+ "style": "expanded",
+ "relativeUrls": true,
+ "sourceMap": false
+ },
+ "nodesass": {
+ "autoPrefix": "",
+ "includePath": "",
+ "indentType": "space",
+ "indentWidth": 2,
+ "outputStyle": "nested",
+ "precision": 5,
+ "relativeUrls": true,
+ "sourceMapRoot": "",
+ "lineFeed": "",
+ "sourceMap": false
+ },
+ "stylus": {
+ "sourceMap": false
+ },
+ "babel": {
+ "sourceMap": false
+ },
+ "coffeescript": {
+ "bare": false,
+ "runtimeMode": "node",
+ "sourceMap": false
+ },
+ "handlebars": {
+ "root": "",
+ "noBOM": false,
+ "name": "",
+ "namespace": "",
+ "knownHelpersOnly": false,
+ "forcePartial": false,
+ "knownHelpers": [],
+ "commonjs": "",
+ "amd": false,
+ "sourceMap": false
+ }
+ },
+ "minifiers": {
+ "css": {
+ "enabled": true,
+ "termSemicolons": true,
+ "gzip": false
+ },
+ "javascript": {
+ "enabled": true,
+ "termSemicolons": true,
+ "gzip": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/MP-TAB/MP-TAB/wwwroot/app.css b/MP-TAB/MP-TAB/wwwroot/app.css
index dee51a93..c731abb5 100644
--- a/MP-TAB/MP-TAB/wwwroot/app.css
+++ b/MP-TAB/MP-TAB/wwwroot/app.css
@@ -1,47 +1,210 @@
-html, body {
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+html,
+body {
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
-
h1:focus {
- outline: none;
+ outline: none;
}
-
-a, .btn-link {
- color: #0071c1;
+a,
+.btn-link {
+ color: #0071c1;
}
-
.btn-primary {
- color: #fff;
- background-color: #1b6ec2;
- border-color: #1861ac;
+ color: #fff;
+ background-color: #1b6ec2;
+ border-color: #1861ac;
}
-
-.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
+.btn:focus,
+.btn:active:focus,
+.btn-link.nav-link:focus,
+.form-control:focus,
+.form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
-
.content {
- padding-top: 1.1rem;
+ padding-top: 1.1rem;
}
-
.valid.modified:not([type=checkbox]) {
- outline: 1px solid #26b050;
+ outline: 1px solid #26b050;
}
-
.invalid {
- outline: 1px solid red;
+ outline: 1px solid red;
}
-
.validation-message {
- color: red;
+ color: red;
}
-
.blazor-error-boundary {
- background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
- padding: 1rem 1rem 1rem 3.7rem;
- color: white;
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
+ padding: 1rem 1rem 1rem 3.7rem;
+ color: white;
}
-
- .blazor-error-boundary::after {
- content: "An error has occurred."
- }
+.blazor-error-boundary::after {
+ content: "An error has occurred.";
+}
+/* Gestione semafori */
+.statusMap {
+ /*background: rgba(0,0,0,0.3);*/
+}
+.statusMap .ui-title,
+.statusMap .ui-li-aside {
+ font-weight: 400;
+ text-transform: uppercase;
+ font-size: 2.5em;
+ line-height: 1.1em;
+ color: #DEDEDE;
+ text-shadow: 2px 2px 4px #000;
+ text-align: center;
+ background: linear-gradient(270deg, rgba(20, 20, 20, 0.7), rgba(100, 100, 100, 0.7), rgba(20, 20, 20, 0.7));
+}
+.statusMap .ui-subtitle {
+ line-height: 1.1em;
+ color: #DEDEDE;
+ text-shadow: 2px 2px 4px #000;
+ text-align: center;
+ background: linear-gradient(270deg, rgba(0, 0, 0, 0.4), rgba(100, 100, 100, 0.4), rgba(0, 0, 0, 0.4));
+}
+.statusMap .ui-footer {
+ color: #CDCDCD;
+ background: linear-gradient(270deg, rgba(10, 10, 10, 0.7), rgba(80, 80, 80, 0.7), rgba(10, 10, 10, 0.7));
+}
+.machBlock {
+ padding: 0 2px 0 2px;
+}
+/* END: gestione layout dinamico mappa... */
+/* area semafori*/
+.semBlinkVe,
+.semFixVe,
+.semFixVe_b,
+.semVe,
+.sVe,
+.semVe_b {
+ background: #009036;
+ background: rgba(0, 255, 80, 0.6);
+ color: #FFFFAA;
+}
+.semBlinkGr,
+.semFixGr,
+.semFixGr_b,
+.semGr,
+.semGr_b {
+ background-color: #bcbcbc;
+ background: rgba(180, 180, 180, 0.6);
+}
+.semGi,
+.sGi {
+ text-align: left;
+ background: #8a8d27;
+ background: rgba(230, 210, 0, 0.6);
+ padding: 0px 4px 0px 4px;
+ color: #000;
+}
+.semGi_b,
+.semFixGi,
+.semFixGi_b {
+ text-align: left;
+ background: #f9ff18;
+ background: rgba(255, 255, 0, 0.8);
+ padding: 0px 4px 0px 4px;
+ color: #333;
+}
+.semBl {
+ text-align: left;
+ background: #000E7A;
+ background: rgba(0, 5, 200, 0.6);
+ padding: 0px 4px 0px 4px;
+ color: #959500;
+}
+.semBl_b,
+.semFixBl,
+.semFixBl_b {
+ text-align: left;
+ background: #243FFF;
+ background: rgba(60, 80, 255, 0.8);
+ padding: 0px 4px 0px 4px;
+ color: #ffff32;
+}
+.semRo {
+ text-align: left;
+ background-color: #7a000e;
+ background: rgba(200, 0, 5, 0.6);
+ padding: 0px 4px 0px 4px;
+ color: #959500;
+}
+.semRo_b,
+.semFixRo,
+.semFixRo_b {
+ text-align: left;
+ background-color: #ff243f;
+ background: rgba(255, 60, 80, 0.8);
+ padding: 0px 4px 0px 4px;
+ color: #ffff32;
+}
+/* semafori con animazione blinking */
+.no-cpu {
+ -moz-transform: translateZ(0);
+ -o-transform: translateZ(0);
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+}
+@-webkit-keyframes blinkBack {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+@-moz-keyframes blinkBack {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+@-o-keyframes blinkBack {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+@keyframes blinkBack {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+.semBlinkGi {
+ background: linear-gradient(270deg, #8a8d27, #f9ff18);
+ background-size: 400% 400%;
+ -webkit-animation: blinkBack 2s ease infinite;
+ -moz-animation: blinkBack 2s ease infinite;
+ -o-animation: blinkBack 2s ease infinite;
+ animation: blinkBack 2s ease infinite;
+}
+.semBlinkRo {
+ background: linear-gradient(270deg, #7a000e, #ff243f);
+ background-size: 400% 400%;
+ -webkit-animation: blinkBack 2s ease infinite;
+ -moz-animation: blinkBack 2s ease infinite;
+ -o-animation: blinkBack 2s ease infinite;
+ animation: blinkBack 2s ease infinite;
+ color: Yellow;
+}
\ No newline at end of file
diff --git a/MP-TAB/MP-TAB/wwwroot/app.less b/MP-TAB/MP-TAB/wwwroot/app.less
new file mode 100644
index 00000000..c55d95bb
--- /dev/null
+++ b/MP-TAB/MP-TAB/wwwroot/app.less
@@ -0,0 +1,48 @@
+html, body {
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+}
+
+h1:focus {
+ outline: none;
+}
+
+a, .btn-link {
+ color: #0071c1;
+}
+
+.btn-primary {
+ color: #fff;
+ background-color: #1b6ec2;
+ border-color: #1861ac;
+}
+
+.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
+ box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
+}
+
+.content {
+ padding-top: 1.1rem;
+}
+
+.valid.modified:not([type=checkbox]) {
+ outline: 1px solid #26b050;
+}
+
+.invalid {
+ outline: 1px solid red;
+}
+
+.validation-message {
+ color: red;
+}
+
+.blazor-error-boundary {
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
+ padding: 1rem 1rem 1rem 3.7rem;
+ color: white;
+}
+
+ .blazor-error-boundary::after {
+ content: "An error has occurred."
+ }
+
diff --git a/MP-TAB/MP-TAB/wwwroot/app.min.css b/MP-TAB/MP-TAB/wwwroot/app.min.css
new file mode 100644
index 00000000..bffcba22
--- /dev/null
+++ b/MP-TAB/MP-TAB/wwwroot/app.min.css
@@ -0,0 +1 @@
+html,body{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;}h1:focus{outline:0;}a,.btn-link{color:#0071c1;}.btn-primary{color:#fff;background-color:#1b6ec2;border-color:#1861ac;}.btn:focus,.btn:active:focus,.btn-link.nav-link:focus,.form-control:focus,.form-check-input:focus{box-shadow:0 0 0 .1rem white,0 0 0 .25rem #258cfb;}.content{padding-top:1.1rem;}.valid.modified:not([type=checkbox]){outline:1px solid #26b050;}.invalid{outline:1px solid #f00;}.validation-message{color:#f00;}.blazor-error-boundary{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem,#b32121;padding:1rem 1rem 1rem 3.7rem;color:#fff;}.blazor-error-boundary::after{content:"An error has occurred.";}.statusMap .ui-title,.statusMap .ui-li-aside{font-weight:400;text-transform:uppercase;font-size:2.5em;line-height:1.1em;color:#dedede;text-shadow:2px 2px 4px #000;text-align:center;background:linear-gradient(270deg,rgba(20,20,20,.7),rgba(100,100,100,.7),rgba(20,20,20,.7));}.statusMap .ui-subtitle{line-height:1.1em;color:#dedede;text-shadow:2px 2px 4px #000;text-align:center;background:linear-gradient(270deg,rgba(0,0,0,.4),rgba(100,100,100,.4),rgba(0,0,0,.4));}.statusMap .ui-footer{color:#cdcdcd;background:linear-gradient(270deg,rgba(10,10,10,.7),rgba(80,80,80,.7),rgba(10,10,10,.7));}.machBlock{padding:0 2px 0 2px;}.semBlinkVe,.semFixVe,.semFixVe_b,.semVe,.sVe,.semVe_b{background:#009036;background:rgba(0,255,80,.6);color:#ffa;}.semBlinkGr,.semFixGr,.semFixGr_b,.semGr,.semGr_b{background-color:#bcbcbc;background:rgba(180,180,180,.6);}.semGi,.sGi{text-align:left;background:#8a8d27;background:rgba(230,210,0,.6);padding:0 4px 0 4px;color:#000;}.semGi_b,.semFixGi,.semFixGi_b{text-align:left;background:#f9ff18;background:rgba(255,255,0,.8);padding:0 4px 0 4px;color:#333;}.semBl{text-align:left;background:#000e7a;background:rgba(0,5,200,.6);padding:0 4px 0 4px;color:#959500;}.semBl_b,.semFixBl,.semFixBl_b{text-align:left;background:#243fff;background:rgba(60,80,255,.8);padding:0 4px 0 4px;color:#ffff32;}.semRo{text-align:left;background-color:#7a000e;background:rgba(200,0,5,.6);padding:0 4px 0 4px;color:#959500;}.semRo_b,.semFixRo,.semFixRo_b{text-align:left;background-color:#ff243f;background:rgba(255,60,80,.8);padding:0 4px 0 4px;color:#ffff32;}.no-cpu{-moz-transform:translateZ(0);-o-transform:translateZ(0);-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);}@-webkit-keyframes blinkBack{0%{background-position:0% 50%;}50%{background-position:100% 50%;}100%{background-position:0% 50%;}}@-moz-keyframes blinkBack{0%{background-position:0% 50%;}50%{background-position:100% 50%;}100%{background-position:0% 50%;}}@-o-keyframes blinkBack{0%{background-position:0% 50%;}50%{background-position:100% 50%;}100%{background-position:0% 50%;}}@keyframes blinkBack{0%{background-position:0% 50%;}50%{background-position:100% 50%;}100%{background-position:0% 50%;}}.semBlinkGi{background:linear-gradient(270deg,#8a8d27,#f9ff18);background-size:400% 400%;-webkit-animation:blinkBack 2s ease infinite;-moz-animation:blinkBack 2s ease infinite;-o-animation:blinkBack 2s ease infinite;animation:blinkBack 2s ease infinite;}.semBlinkRo{background:linear-gradient(270deg,#7a000e,#ff243f);background-size:400% 400%;-webkit-animation:blinkBack 2s ease infinite;-moz-animation:blinkBack 2s ease infinite;-o-animation:blinkBack 2s ease infinite;animation:blinkBack 2s ease infinite;color:#ff0;}
\ No newline at end of file