Files
CMS-MTConn/MTC_Adapter/MTC/baseUtils.cs
T
2018-11-14 18:54:04 +01:00

1041 lines
25 KiB
C#

using NLog;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Linq;
namespace MTC
{
public class baseUtils
{
/// <summary>
/// classe logger
/// </summary>
public static Logger lg;
#region area gestione conf file
/// <summary>
/// legge conf in formato char
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static char CRC(string key)
{
char answ = '-';
try
{
answ = ConfigurationManager.AppSettings[key].ToCharArray()[0];
}
catch
{ }
return answ;
}
/// <summary>
/// legge conf in formato stringa
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string CRS(string key)
{
string answ = "";
try
{
answ = ConfigurationManager.AppSettings[key].ToString();
}
catch
{ }
return answ;
}
/// <summary>
/// legge conf in formato INT
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static Int32 CRI(string key)
{
int answ = 0;
try
{
answ = Convert.ToInt32(CRS(key));
}
catch
{ }
return answ;
}
/// <summary>
/// legge conf in formato BOOLean
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool CRB(string key)
{
bool answ = false;
try
{
answ = Convert.ToBoolean(CRS(key));
}
catch
{ }
return answ;
}
#endregion
#region area manipolazione bit/byte
/// <summary>
/// verifica se un dato bit sia alzato (come flag di strobe)
/// </summary>
/// <param name="value">valore da testare</param>
/// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità AND</param>
/// <returns></returns>
public static bool IsSetAll(Strobe value, Strobe flag)
{
return ((value & flag) == flag);
}
/// <summary>
/// verifica se un dato bit sia alzato (come flag di strobe)
/// </summary>
/// <param name="value">valore da testare</param>
/// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità OR</param>
/// <returns></returns>
public static bool IsSetAny(Strobe value, Strobe flag)
{
return ((value & flag) != 0);
}
/// <summary>
/// verifica se un dato bit sia alzato (come flag di strobe)
/// </summary>
/// <param name="value">valore da testare</param>
/// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità AND</param>
/// <returns></returns>
public static bool IsSetAll(StatusBitMap value, StatusBitMap flag)
{
return ((value & flag) == flag);
}
/// <summary>
/// verifica se un dato bit sia alzato (come flag di strobe)
/// </summary>
/// <param name="value">valore da testare</param>
/// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità OR</param>
/// <returns></returns>
public static bool IsSetAny(StatusBitMap value, StatusBitMap flag)
{
return ((value & flag) != 0);
}
/// <summary>
/// formatta un numero in forma binaria 0/1
/// </summary>
/// <param name="valore"></param>
/// <returns></returns>
public static string binaryForm(int valore)
{
string answ = "";
try
{
answ = string.Format(new BinaryFormatter(), "{0:B}", valore);
}
catch
{ }
return answ;
}
/// <summary>
/// imposta un bit al valore richiesto duplicando il valore IN come OUT
/// </summary>
/// <param name="original">valore originale da aggiornare</param>
/// <param name="bitBool">valore richiesto x il bit (0/1)</param>
/// <param name="bitIndex">indice bit, 0 based (es: 0..31 per 32bit)</param>
/// <returns></returns>
public static byte[] setBitOnStFlag(byte[] original, bool bitBool, int bitIndex)
{
int bitVal = 0;
if (bitBool)
{
bitVal = 1;
}
// risposta è identica ad originale...
byte[] answ = original;
// verifico se il bit è 0/1b
if (bitVal <= 1 && bitVal >= 0)
{
// verifico se si possa aggiornare il bit richiesto (<= al totale dei bit...)
if (bitIndex <= original.Length * 8 - 1)
{
// calcolo byte
int byteIndex = bitIndex / 8;
// bit nel byte
int bitInByteIndex = bitIndex % 8;
// bit richiesto
byte mask = (byte)(bitVal << bitInByteIndex);
// imposto!
answ[byteIndex] |= mask;
}
}
return answ;
}
/// <summary>
/// Converte un bitarray a byte[]
/// </summary>
/// <param name="bits"></param>
/// <returns></returns>
public static byte[] ToByteArray(BitArray bits)
{
int numBytes = bits.Count / 8;
if (bits.Count % 8 != 0)
{
numBytes++;
}
byte[] bytes = new byte[numBytes];
int byteIndex = 0, bitIndex = 0;
for (int i = 0; i < bits.Count; i++)
{
if (bits[i])
{
bytes[byteIndex] |= (byte)(1 << (7 - bitIndex));
}
bitIndex++;
if (bitIndex == 8)
{
bitIndex = 0;
byteIndex++;
}
}
return bytes;
}
/// <summary>
/// Scrittura dictionary su file
/// </summary>
/// <param name="dictionary"></param>
/// <param name="file"></param>
public static void WriteBin(Dictionary<string, string> dictionary, string file)
{
using (FileStream fs = File.OpenWrite(file))
using (BinaryWriter writer = new BinaryWriter(fs))
{
// Put count.
writer.Write(dictionary.Count);
// Write pairs.
foreach (var pair in dictionary)
{
writer.Write(pair.Key);
writer.Write(pair.Value);
}
}
}
/// <summary>
/// Lettura dictionary da file
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static Dictionary<string, string> ReadBin(string file)
{
var result = new Dictionary<string, string>();
// verifico file esista...
if (!File.Exists(file))
{
FileStream fs = File.Create(file);
fs.Close();
}
using (FileStream fs = File.OpenRead(file))
using (BinaryReader reader = new BinaryReader(fs))
{
// Get count.
int count = 0;
try
{
count = reader.ReadInt32();
}
catch
{ }
// Read in all pairs.
for (int i = 0; i < count; i++)
{
string key = reader.ReadString();
string value = reader.ReadString();
result[key] = value;
}
}
return result;
}
/// <summary>
/// Scrittura dictionary su file
/// </summary>
/// <param name="dictionary"></param>
/// <param name="file"></param>
public static void WritePlain(Dictionary<string, string> dictionary, string file)
{
string dirPath = file.Substring(0, file.LastIndexOf('\\'));
// verifico directory
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
string[] lines = dictionary.OrderBy(i => i.Key).Select(kvp => kvp.Key + ":" + kvp.Value).ToArray();
try
{
File.WriteAllLines(file, lines);
}
catch (Exception exc)
{
lg.Info(exc, string.Format("Errore in scrittura file {0}", file));
}
}
/// <summary>
/// Lettura dictionary da file
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static Dictionary<string, string> ReadPlain(string file)
{
// inizializzo num righe lette...
int numRow = 0;
var result = new Dictionary<string, string>();
// verifico file esista...
if (!File.Exists(file))
{
FileStream fs = File.Create(file);
fs.Close();
}
try
{
string[] lines = File.ReadAllLines(file);
result = lines.Select(l => l.Split(':')).ToDictionary(a => a[0], a => a[1]);
numRow = result.Count;
}
catch
{ }
// se leggesse un valore NON coerente (senza righe) restituisce un file vuoto...
if (numRow == 0)
{
result = new Dictionary<string, string>();
}
return result;
}
#endregion
}
/// <summary>
/// Oggetto timing x archiviazione dati perfomances
/// </summary>
public class TimeRec
{
/// <summary>
/// Codice univoco chiamata: tipo R4 (read 4 byte), W2 (write 2 Byte)
/// </summary>
public string codCall;
/// <summary>
/// Num chiamate totale
/// </summary>
public int numCall;
/// <summary>
/// Tempo medio chiamata
/// </summary>
public double avgMsec
{
get
{
return totMsec.TotalMilliseconds / numCall;
}
}
/// <summary>
/// Totale Msec accumulati
/// </summary>
public TimeSpan totMsec;
/// <summary>
/// Classe record timing
/// </summary>
public TimeRec()
{
codCall = "";
numCall = 0;
totMsec = new TimeSpan(0);
}
/// <summary>
/// Classe record timing
/// </summary>
/// <param name="codice"></param>
/// <param name="ticks"></param>
public TimeRec(string codice, long nTicks)
{
codCall = codice;
numCall = 1;
totMsec = new TimeSpan(nTicks);
}
}
/// <summary>
/// Classe gestione valori campionati su periodo
/// </summary>
public class sampleVect
{
/// <summary>
/// Dimensione finestra di campionamento (secondi)
/// </summary>
protected int windSize;
/// <summary>
/// vettore valori temporali della serie
/// </summary>
protected List<DateTime> lTime;
/// <summary>
/// vettore valoti puntuali della serie
/// </summary>
protected List<int> lVal;
/// <summary>
/// Inizializzo l'oggetto
/// </summary>
public sampleVect()
{
// init valori default...
windSize = baseUtils.CRI("countWindSize") > 0 ? baseUtils.CRI("countWindSize") : 60;
lTime = new List<DateTime>();
lVal = new List<int>();
}
/// <summary>
/// Conteggio elementi
/// </summary>
protected int numElem
{
get
{
int answ = 0;
try
{
answ = lTime.Count;
}
catch
{ }
return answ;
}
}
/// <summary>
/// Verifica ampiezza finestra valori First-Last
/// </summary>
protected double flWindSize
{
get
{
double answ = 0;
if (numElem > 1)
{
answ = lTime.Last().Subtract(lTime[0]).TotalSeconds;
}
return answ;
}
}
/// <summary>
/// Verifica ampiezza finestra valori Second-Last
/// </summary>
protected double slWindSize
{
get
{
double answ = 0;
if (numElem > 2) // altrimenti SE non ne ho almeno 3 NON posso avere secondo/ultimo...
{
answ = lTime.Last().Subtract(lTime[1]).TotalSeconds;
}
return answ;
}
}
/// <summary>
/// Aggiunge un valore alla serie ed eventualmente elimina i valori superflui a garantirne una finestra temporale valida
/// </summary>
/// <param name="tempo"></param>
/// <param name="valore"></param>
public void addValue(DateTime tempo, int valore)
{
lTime.Add(tempo);
lVal.Add(valore);
// verifico se siano da accorciare le serie... ovvero i 2 intervalli ENTRAMBI sono superiori al periodo minimo (in tal caso riduco..
while (flWindSize > windSize && slWindSize > windSize)
{
// elimino i 2 valori + vecchi
lTime.RemoveAt(0);
lVal.RemoveAt(0);
// ora ricontrollo...
}
}
/// <summary>
/// Calcola il valore mediano...
/// </summary>
public double vcMedian
{
get
{
double answ = 0;
// restituisce la mediana SE valida, altrimenti null...
if (numElem > 2 && flWindSize > windSize)
{
try
{
// calcolo mediana!
//answ = Statistics.Median(lVal.ToArray());
// rif: https://blogs.msmvps.com/deborahk/linq-mean-median-and-mode/
var sortedNumbers = lVal.OrderBy(n => n);
int numCount = lVal.Count;
int indice50 = lVal.Count / 2;
if ((numCount % 2) == 0)
{
answ = ((sortedNumbers.ElementAt(indice50) + sortedNumbers.ElementAt(indice50 - 1)) / 2);
}
else
{
answ = sortedNumbers.ElementAt(indice50);
}
}
catch
{ }
}
return answ;
}
}
/// <summary>
/// Verifica se la vc sia valida (ovvero almeno 2 valori e intervallo > window richiesta)
/// </summary>
public bool vcValid
{
get
{
return (flWindSize > windSize && numElem > 1);
}
}
}
public static class TimingData
{
public static List<TimeRec> results = new List<TimeRec>();
/// <summary>
/// aggiorno vettore aggiungendo risultato
/// </summary>
/// <param name="codice"></param>
/// <param name="ticks"></param>
public static void addResult(string codice, long ticks)
{
if (results.Count == 0)
{
results.Add(new TimeRec(codice, ticks));
}
int indice = -1;
for (int i = 0; i < results.Count; i++)
{
// se il codice è quello cercato...
if (results[i].codCall == codice)
{
indice = i;
}
}
// se c'è aggiorno...
if (indice >= 0)
{
results[indice].numCall++;
results[indice].totMsec = results[indice].totMsec.Add(new TimeSpan(ticks));
}
// altrimenti aggiungo...
else
{
results.Add(new TimeRec(codice, ticks));
}
}
/// <summary>
/// Resetta i dati registrati (ad avvio adapter...)
/// </summary>
public static void resetData()
{
results = new List<TimeRec>();
}
}
/// <summary>
/// Gestione Endianness
/// </summary>
public static class Endian
{
/// <summary>
/// Scambia MSB/LSB per 16bit
/// </summary>
/// <param name="inValue"></param>
/// <returns></returns>
public static UInt16 SwapUInt16(UInt16 inValue)
{
return (UInt16)(((inValue & 0xff00) >> 8) |
((inValue & 0x00ff) << 8));
}
/// <summary>
/// Scambia MSB/LSB per 32bit
/// </summary>
/// <param name="inValue"></param>
/// <returns></returns>
public static UInt32 SwapUInt32(UInt32 inValue)
{
return ((inValue & 0xff000000) >> 24) |
((inValue & 0x00ff0000) >> 8) |
((inValue & 0x0000ff00) << 8) |
((inValue & 0x000000ff) << 24);
}
}
public enum tipoAdapter
{
/// <summary>
/// Adapter DB
/// </summary>
DB,
/// <summary>
/// Adapter generico/demo
/// </summary>
DEMO,
/// <summary>
/// Adapter ESAGV (SCM)
/// </summary>
ESAGV,
/// <summary>
/// adapter FANUC (CMS)
/// </summary>
FANUC,
/// <summary>
/// Adapter non specificato
/// </summary>
ND,
/// <summary>
/// Adapter OSAI
/// </summary>
OSAI,
/// <summary>
/// Adapter SIEMENS (CMS)
/// </summary>
SIEMENS
}
public enum gatherCycle
{
/// <summary>
/// Very High Frequency (solo x invii...)
/// </summary>
VHF,
/// <summary>
/// lettura dati ad alta frequenza
/// </summary>
HF,
/// <summary>
/// lettura dati standard
/// </summary>
MF,
/// <summary>
/// lettura dati bassa freq
/// </summary>
LF,
/// <summary>
/// lettura dati bassissima priorità (re-sync stato allarmi)
/// </summary>
VLF
}
/// <summary>
/// informazioni di produzione
/// </summary>
public struct prodData
{
public string Operator;
public bool Status;
public int AccTime;
public int Power;
public string FuncMode;
public bool EmrStop;
public string MessageCode;
public string MessageText;
}
public struct PathData
{
public int PathSel;
public string PathType; // LAVOR/ASSERV
public string RunMode;
public string ExeMode;
public int pzTot;
public string ProgramName;
public string ProgrRow;
public string PartId;
public string ActiveAxes;
public string CodG_Act;
public string SubMode;
public int PathFeedrate;
public int PathFeedrateOver;
public int PathRapidOver;
public int PathJogOver;
public int PathSpindleOver_01;
public int PathSpindleOver_02;
public int PathSpindleOver_03;
public int PathSpindleOver_04;
public position PathPosAct;
}
public struct UnOpData
{
public int UnOpSel;
public int UnOpToolId;
public int UnOpNumCU;
public string UnOpStatus;
public int UnOpVitaRes;
public int UnOpSpeed;
public int UnOpLoad;
public int UnOpAccTime;
}
public struct AxisData
{
public int AxisSel;
public string AxisMainProc;
public bool AxisIsMaster;
public string AxisMastId;
public string AxisType;
public string AxisDir;
public int AxisLoad;
public int AxisPosAct;
public int AxisPosTgt;
public int AxisFeedAct;
public int AxisFeedOver;
public string AxisAccel;
public string AxisAccTime;
public string AxisBattery;
public string DistDone;
public string InvDDone;
}
/// <summary>
/// Vettore completo posizione (X-Y-Z con versori i-j-k)
/// </summary>
public class position
{
public float x;
public float y;
public float z;
public float i;
public float j;
public float k;
public position()
{
x = 0;
y = 0;
z = 0;
i = 0;
j = 0;
k = 0;
}
}
/// <summary>
/// Dati Prod SCM (per decodifica)
/// </summary>
public class datiProdSCM
{
public string area;
public string fileName;
public string dimensioni;
public DateTime start;
public DateTime stop;
public TimeSpan tEff;
public TimeSpan tTot;
public int qta;
public TimeSpan tMed;
public datiProdSCM()
{
area = "";
fileName = "";
dimensioni = "";
start = DateTime.Now;
stop = DateTime.Now;
tEff = new TimeSpan(0);
tTot = new TimeSpan(0);
qta = 0;
tMed = new TimeSpan(0);
}
/// <summary>
/// crea un nuovo oggetto a partire da un array di stringhe
/// </summary>
/// <param name="valori"></param>
public datiProdSCM(string[] valori)
{
try
{
area = valori[0];
fileName = valori[1];
dimensioni = string.Format("{0}x{1}x{2}", valori[3], valori[4], valori[5]);
start = DateTime.Today.AddHours(Convert.ToInt16(valori[6])).AddMinutes(Convert.ToInt16(valori[7])).AddSeconds(Convert.ToInt16(valori[8]));
stop = DateTime.Today.AddHours(Convert.ToInt16(valori[9])).AddMinutes(Convert.ToInt16(valori[10])).AddSeconds(Convert.ToInt16(valori[11]));
// se ore == 0 --> aggiungo 1 gg!!!
if (Convert.ToInt16(valori[9]) == 0)
{
stop.AddDays(1);
}
tEff = new TimeSpan(Convert.ToInt16(valori[12]), Convert.ToInt16(valori[13]), Convert.ToInt16(valori[14]));
tTot = new TimeSpan(Convert.ToInt16(valori[15]), Convert.ToInt16(valori[16]), Convert.ToInt16(valori[17]));
qta = Convert.ToInt16(valori[18]);
tMed = new TimeSpan(Convert.ToInt16(valori[19]), Convert.ToInt16(valori[20]), Convert.ToInt16(valori[22]), Convert.ToInt16(valori[23]));
}
catch
{ }
}
}
/// <summary>
/// Allarme (per decodifica)
/// </summary>
public class allarme
{
/// <summary>
/// Codice
/// </summary>
public string codNum;
/// <summary>
/// Gruppo (CNC/PLC/HMI)
/// </summary>
public string gruppo;
/// <summary>
/// Livello allarme
/// - numerico [001...999]
/// - enum: FAULT=Alarm=900, Warning=500, Info=200, Maintenance=100
/// </summary>
public string livello;
/// <summary>
/// Descrizione in lingua corrente
/// </summary>
public string descrizione;
/// <summary>
/// Descrizione in IT
/// </summary>
public string descrizioneIt;
/// <summary>
/// Descrizione in EN
/// </summary>
public string descrizioneEn;
public allarme()
{
codNum = "";
gruppo = "";
livello = "";
descrizione = "";
descrizioneIt = "";
descrizioneEn = "";
}
/// <summary>
/// Inizializzaizone allarme con tutti i valori della descrizione in 3 lingue...
/// </summary>
/// <param name="_codNum"></param>
/// <param name="_gruppo"></param>
/// <param name="_livello"></param>
/// <param name="_descrizione">Usato per lingua CORRENTE, IT, EN</param>
public allarme(string _codNum, string _gruppo, string _livello, string _descrizione)
{
codNum = _codNum;
gruppo = _gruppo;
livello = _livello;
descrizione = _descrizione;
descrizioneIt = _descrizione;
descrizioneEn = _descrizione;
}
/// <summary>
/// Inizializzaizone allarme con tutti i valori della descrizione in 3 lingue...
/// </summary>
/// <param name="_codNum"></param>
/// <param name="_gruppo"></param>
/// <param name="_livello"></param>
/// <param name="_descrizione"></param>
/// <param name="_descrizioneIt"></param>
/// <param name="_descrizioneEn"></param>
public allarme(string _codNum, string _gruppo, string _livello, string _descrizione, string _descrizioneIt, string _descrizioneEn)
{
codNum = _codNum;
gruppo = _gruppo;
livello = _livello;
descrizione = _descrizione;
descrizioneIt = _descrizioneIt;
descrizioneEn = _descrizioneEn;
}
}
/// <summary>
/// Dato generico (per decodifica)
/// </summary>
public class otherData
{
public string codNum;
public string memAddr;
public string varName;
public string dataType;
public otherData()
{
codNum = "";
memAddr = "";
varName = "";
dataType = "";
}
public otherData(string _codNum, string _memAddr, string _varName, string _dataType)
{
codNum = _codNum;
memAddr = _memAddr;
varName = _varName;
dataType = _dataType;
}
}
/// <summary>
/// Strobe: contiene il set di strobe di comunicazione
///
/// rif: http://stackoverflow.com/questions/17209054/parse-bits-in-a-byte-to-enum
/// </summary>
[Flags]
public enum Strobe : int
{
NONE = 0,
M_CODE = 1 << 0,
S_CODE = 1 << 1,
T_CODE = 1 << 2,
PZ_OK = 1 << 3,
PZ_KO = 1 << 4,
FEED_SPEED = 1 << 5,
POS_ACT = 1 << 6,
SP07 = 1 << 7,
SP08 = 1 << 8,
SP09 = 1 << 9,
SP10 = 1 << 10,
SP11 = 1 << 11,
SP12 = 1 << 12,
SP13 = 1 << 13,
SP14 = 1 << 14,
SP15 = 1 << 15,
SP16 = 1 << 16,
SP17 = 1 << 17,
SP18 = 1 << 18,
SP19 = 1 << 19,
SP20 = 1 << 20,
SP21 = 1 << 21,
SP22 = 1 << 22,
SP23 = 1 << 23,
SP24 = 1 << 24,
SP25 = 1 << 25,
SP26 = 1 << 26,
SP27 = 1 << 27,
SP28 = 1 << 28,
SP29 = 1 << 29,
SP30 = 1 << 30,
SP31 = 1 << 31
}
/// <summary>
/// StFlag8: set di 8 bit (1 word) contente semaforo di variabili
/// </summary>
[Flags]
public enum StFlag8 : int
{
NONE = 0,
B0 = 1 << 0,
B1 = 1 << 1,
B2 = 1 << 2,
B3 = 1 << 3,
B4 = 1 << 4,
B5 = 1 << 5,
B6 = 1 << 6,
B7 = 1 << 7
}
/// <summary>
/// StFlag32: set di 32 bit (4 word) contente semaforo di variabili
/// </summary>
[Flags]
public enum StFlag32 : int
{
NONE = 0,
B00 = 1 << 0,
B01 = 1 << 1,
B02 = 1 << 2,
B03 = 1 << 3,
B04 = 1 << 4,
B05 = 1 << 5,
B06 = 1 << 6,
B07 = 1 << 7,
B08 = 1 << 8,
B09 = 1 << 9,
B10 = 1 << 10,
B11 = 1 << 11,
B12 = 1 << 12,
B13 = 1 << 13,
B14 = 1 << 14,
B15 = 1 << 15,
B16 = 1 << 16,
B17 = 1 << 17,
B18 = 1 << 18,
B19 = 1 << 19,
B20 = 1 << 20,
B21 = 1 << 21,
B22 = 1 << 22,
B23 = 1 << 23,
B24 = 1 << 24,
B25 = 1 << 25,
B26 = 1 << 26,
B27 = 1 << 27,
B28 = 1 << 28,
B29 = 1 << 29,
B30 = 1 << 30,
B31 = 1 << 31
}
/// <summary>
/// StatusBitMap: contiene il set di semafori/flag x status + allarmi (x classi)
/// </summary>
[Flags]
public enum StatusBitMap : int
{
NONE = 0,
ESTOP = 1 << 0,
RM_AUTO = 1 << 1,
RM_MANUAL = 1 << 2,
RM_MDI = 1 << 3,
RM_EDIT = 1 << 4,
EM_RUN = 1 << 5,
EM_READY = 1 << 6,
EM_STOP = 1 << 7,
EM_FEEDHOLD = 1 << 8,
HM = 1 << 9,
ST11 = 1 << 10,
ST12 = 1 << 11,
ST13 = 1 << 12,
ST14 = 1 << 13,
ST15 = 1 << 14,
ST16 = 1 << 15,
AL01 = 1 << 16,
AL02 = 1 << 17,
AL03 = 1 << 18,
AL04 = 1 << 19,
AL05 = 1 << 20,
AL06 = 1 << 21,
AL07 = 1 << 22,
AL08 = 1 << 23,
AL09 = 1 << 24,
AL10 = 1 << 25,
AL11 = 1 << 26,
AL12 = 1 << 27,
AL13 = 1 << 28,
AL14 = 1 << 29,
AL15 = 1 << 30,
AL16 = 1 << 31
}
}