You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
927 lines
49 KiB
927 lines
49 KiB
using DataService;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Data.SqlTypes;
|
|
using System.IO;
|
|
using System.IO.MemoryMappedFiles;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
|
|
namespace DatabaseLib
|
|
{
|
|
public class HDAIOHelper
|
|
{
|
|
static int[] dataLen = new int[] { 5, 5, 5, 5, 6, 6, 8, 8, 8, 8, 8, 8 };
|
|
|
|
static string m_Path = @"D:\HDA";
|
|
|
|
static HDAIOHelper()
|
|
{
|
|
m_Path = DataHelper.HdaPath;
|
|
}
|
|
|
|
public static bool FindFile(DateTime date)
|
|
{
|
|
if (Directory.Exists(m_Path))
|
|
{
|
|
return File.Exists(string.Concat(m_Path, "\\", date.Year.ToString(), "-", date.Month.ToString(), ".bin"));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static bool CreateFile(int year, int month)
|
|
{
|
|
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString(), ".bin");
|
|
try
|
|
{
|
|
if (Directory.Exists(m_Path))
|
|
{
|
|
if (File.Exists(path))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Directory.CreateDirectory(m_Path);
|
|
}
|
|
using (var stream = File.Create(path, 0x100))
|
|
{
|
|
stream.Write(new byte[0x100], 0, 0x100);
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
DataHelper.AddErrorLog(err);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool GetRangeFromDatabase(short? ID, ref DateTime start, ref DateTime end)
|
|
{
|
|
using (var reader = DataHelper.Instance.ExecuteReader("SELECT MIN(TIMESTAMP),MAX(TIMESTAMP) FROM LOG_HDATA" + (ID.HasValue ? " WHERE ID=" + ID.Value : "")))
|
|
{
|
|
if (reader != null)
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
if (!reader.IsDBNull(0))
|
|
start = reader.GetDateTime(0);
|
|
if (!reader.IsDBNull(1))
|
|
end = reader.GetDateTime(1);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
//start = end = DateTime.MinValue;
|
|
return false;
|
|
}
|
|
|
|
public static void BackUpFile(DateTime date)
|
|
{
|
|
lock (typeof(HDAIOHelper))
|
|
{
|
|
if (WriteToFile(date.AddDays(-1)) == 0)
|
|
{
|
|
DataHelper.Instance.ExecuteNonQuery(string.Format("DELETE FROM LOG_HDATA WHERE [TIMESTAMP]<='{0}';", date.ToShortDateString()));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static int WriteToFile(DateTime date)//每天凌晨写入昨天的数据到文件,可以考虑用服务或计划任务;数据库只保留当天的记录;调度程序负责删除过期记录;历史数据应支持合并
|
|
{
|
|
int year = date.Year; int month = date.Month; int day = date.Day;
|
|
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString(), ".bin");
|
|
if (CreateFile(year, month))//如该月文件不存在,则创建;否则写入
|
|
{
|
|
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
|
{
|
|
stream.Seek(day * 8, SeekOrigin.Begin);//先读入索引区,定位到该日期的指针
|
|
byte[] bits = new byte[8];
|
|
stream.Read(bits, 0, 8);//如果该位置指针为>0的正数,说明该区域已有数据
|
|
if (BitConverter.ToInt64(bits, 0) > 0) return -1;
|
|
}
|
|
using (var dataReader = DataHelper.Instance.ExecuteProcedureReader("WRITEHDATA", DataHelper.CreateParam("@DATE", SqlDbType.DateTime, date)))
|
|
{
|
|
if (dataReader == null) return -10;
|
|
else
|
|
{
|
|
dataReader.Read();
|
|
int cont = dataReader.GetInt32(0);//读入标签数量
|
|
if (cont == 0) return -2;
|
|
string path2 = path + ".temp";
|
|
try
|
|
{
|
|
File.Copy(path, path2, true);//先把原文件全部复制到临时文件
|
|
//Stopwatch sw = Stopwatch.StartNew();
|
|
using (FileStream stream = File.Open(path2, FileMode.Open))
|
|
{
|
|
//w.Seek(8 + day * 8, SeekOrigin.Begin);
|
|
//w.Seek(0x100, SeekOrigin.Begin);
|
|
long start = stream.Seek(0, SeekOrigin.End);//定位到文件末尾
|
|
long end = 0;
|
|
using (BinaryWriter w = new BinaryWriter(stream))
|
|
{
|
|
w.Write(new SqlDateTime(date).DayTicks);//写入日期
|
|
w.Write(cont);///写入标签数量
|
|
int count = dataReader.GetInt32(1);
|
|
w.Write(count);
|
|
HDataFormat[] list = new HDataFormat[count];
|
|
if (dataReader.NextResult())
|
|
{
|
|
int p = 0;
|
|
int x = 0;
|
|
while (dataReader.Read())//写入标签元数据
|
|
{
|
|
short id = dataReader.GetInt16(0);//ID号
|
|
byte type = dataReader.GetByte(1);//数据类型
|
|
int cn = dataReader.GetInt32(2);//标签个数
|
|
//list[x].ID = id;
|
|
list[x].Type = (DataType)type;
|
|
list[x].Count = cn;
|
|
//list[x].Offset = p;
|
|
w.Write(id);
|
|
w.Write(type);
|
|
w.Write(cn);
|
|
w.Write(p);
|
|
p += cn * dataLen[type];
|
|
x++;
|
|
}
|
|
if (dataReader.NextResult())
|
|
{
|
|
for (int i = 0; i < list.Length; i++)
|
|
{
|
|
int len = list[i].Count;
|
|
for (int j = 0; j < len; j++)
|
|
{
|
|
if (dataReader.Read())
|
|
{
|
|
w.Write(dataReader.GetTimeTick(0));
|
|
switch (list[i].Type)
|
|
{
|
|
case DataType.BOOL:
|
|
w.Write(dataReader.GetFloat(1) > 0);
|
|
break;
|
|
case DataType.BYTE:
|
|
w.Write((byte)dataReader.GetFloat(1));
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
w.Write((short)dataReader.GetFloat(1));
|
|
break;
|
|
case DataType.INT:
|
|
w.Write((int)dataReader.GetFloat(1));
|
|
break;
|
|
case DataType.FLOAT:
|
|
w.Write(dataReader.GetFloat(1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
end = stream.Position;//文件的结尾,总长度
|
|
w.Seek((day - 1) * 8, SeekOrigin.Begin);//定位到索引区
|
|
w.Write(start);//写入当日指针
|
|
w.Write(end);//写入下一日指针
|
|
//w.Close();
|
|
}
|
|
}
|
|
File.Copy(path2, path, true);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
DataHelper.AddErrorLog(err);
|
|
return -3;
|
|
}
|
|
finally
|
|
{
|
|
if (File.Exists(path2))
|
|
File.Delete(path2);
|
|
}
|
|
//dataReader.Close();
|
|
return 0;
|
|
/*写入失败,则将备份文件还原;数据库不做删除动作,保留记录,次日服务检查数据文件是否存在,不存在则合并写入
|
|
可在服务内建XML文件保存失败记录的日期列表,以便还原;用File.Mov;定时间隔、开始时间也可XML定义。
|
|
先备份二进制归档库,再加载数据库数据,写入文件;如成功,删除数据库当日记录并删除备份文件
|
|
sw.Stop();
|
|
* if (sw.ElapsedTicks > 0) { }
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
return -10;
|
|
}
|
|
|
|
public static IEnumerable<HistoryData> LoadFromFile(DateTime start, DateTime end, bool sdt = false)
|
|
{
|
|
//Stopwatch sw = Stopwatch.StartNew();
|
|
//文件的组织格式:头文件:31,ln为间隔日期,position为指向日期段的指针,sizes为日期段的长度。
|
|
//每日的抬头:按ID次序,包含每个TAG的数量,arr为每个日期所有的标签、每标签数量、数据类型、位置指针。
|
|
//按时间排序,每个标签的值、时间戳。
|
|
string path = string.Concat(m_Path, "\\", start.Year.ToString(), "-", start.Month.ToString(), sdt ? ".sdt" : ".bin");
|
|
if (!File.Exists(path)) yield break;
|
|
int day1 = start.Day;
|
|
int startTicks = new SqlDateTime(start).TimeTicks;
|
|
int endTicks = new SqlDateTime(end).TimeTicks;
|
|
int ln = end.Day - day1 + 1;
|
|
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
|
{
|
|
BinaryReader reader = new BinaryReader(stream);
|
|
long[] positions = new long[ln + 1];
|
|
long[] sizes = new long[ln];
|
|
stream.Seek((day1 - 1) * 8, SeekOrigin.Begin);
|
|
positions[0] = reader.ReadInt64();
|
|
for (int i = 0; i < ln; i++)
|
|
{
|
|
positions[i + 1] = reader.ReadInt64();
|
|
sizes[i] = positions[i + 1] - positions[i];//每一天数据的长度
|
|
}
|
|
//reader.Close();
|
|
HistoryData data = HistoryData.Empty;
|
|
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, Guid.NewGuid().ToString(), 0, MemoryMappedFileAccess.Read,
|
|
HandleInheritability.Inheritable, false))
|
|
{
|
|
for (int k = 0; k < ln; k++)
|
|
{
|
|
if (positions[k] < 0x100 || sizes[k] <= 0 || positions[k] + sizes[k] > stream.Length)
|
|
continue;
|
|
using (MemoryMappedViewAccessor acc = mapp.CreateViewAccessor(positions[k], sizes[k], MemoryMappedFileAccess.Read))
|
|
{
|
|
long pos = 0;
|
|
int day = acc.ReadInt32(pos);
|
|
pos += 8;
|
|
int count = acc.ReadInt32(pos);
|
|
pos += 4;
|
|
HDataFormat[] arr = new HDataFormat[count];
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
arr[i].ID = acc.ReadInt16(pos);
|
|
pos += 2;
|
|
arr[i].Type = (DataType)acc.ReadByte(pos);
|
|
pos++;
|
|
arr[i].Count = acc.ReadInt32(pos);//4个字节是预留
|
|
pos += 8;
|
|
}
|
|
long tempos = pos;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
int con = arr[i].Count;
|
|
int j = 0;
|
|
pos = tempos + acc.ReadInt32(i * 11 + 19);
|
|
long pf = pos;
|
|
DataType type = arr[i].Type;
|
|
int len = dataLen[(int)type];
|
|
if (k == 0) //判断是否为起始日期或结束日期
|
|
{
|
|
int ind = BinarySearchTime(acc, pf, con, len, startTicks);
|
|
if (ind < 0) ind = ~ind;
|
|
j += ind;
|
|
pos += ind * len;
|
|
}
|
|
if (k == ln - 1)
|
|
{
|
|
int index = BinarySearchTime(acc, pf, con, len, endTicks);
|
|
con = index >= 0 ? index : ~index;
|
|
}
|
|
while (j++ < con)
|
|
{
|
|
data.ID = arr[i].ID;
|
|
data.TimeStamp = new SqlDateTime(day, acc.ReadInt32(pos)).Value;
|
|
pos += 4;
|
|
switch (type)
|
|
{
|
|
case DataType.BOOL:
|
|
data.Value.Boolean = acc.ReadBoolean(pos);
|
|
pos++;
|
|
break;
|
|
case DataType.BYTE:
|
|
data.Value.Byte = acc.ReadByte(pos);
|
|
pos++;
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
data.Value.Int16 = acc.ReadInt16(pos);
|
|
pos += 2;
|
|
break;
|
|
case DataType.INT:
|
|
data.Value.Int32 = acc.ReadInt32(pos);
|
|
pos += 4;
|
|
break;
|
|
case DataType.FLOAT:
|
|
data.Value.Single = acc.ReadSingle(pos);
|
|
pos += 4;
|
|
break;
|
|
}
|
|
yield return data;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
public static IEnumerable<HistoryData> LoadFromFile(DateTime start, DateTime end, short ID, bool sdt = false)
|
|
{
|
|
string path = string.Concat(m_Path, "\\", start.Year.ToString(), "-", start.Month.ToString(), sdt ? ".sdt" : ".bin");//bin-sdt
|
|
if (!File.Exists(path)) yield break;
|
|
int day1 = start.Day;
|
|
int startTicks = new SqlDateTime(start).TimeTicks;//开始日期部分的4位数据
|
|
int endTicks = new SqlDateTime(end).TimeTicks;
|
|
int ln = end.Day - day1 + 1;//日期天数
|
|
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
|
{
|
|
long filelen = stream.Length;//文件长度
|
|
BinaryReader reader = new BinaryReader(stream);
|
|
long[] positions = new long[ln];//每日数据指针(指向第一条数据,包括当日数据索引区)
|
|
stream.Seek((day1 - 1) * 8, SeekOrigin.Begin);///找到对应的开始日期索引位置
|
|
|
|
for (int i = 0; i < ln; i++)
|
|
{
|
|
positions[i] = reader.ReadInt64();//读入时间段内每日数据长度值
|
|
}
|
|
long[] sizes = new long[ln];
|
|
for (int i = 0; i < ln; i++)
|
|
{
|
|
if (positions[i] >= filelen) break;//如果读入长度超过文件大小则退出
|
|
stream.Seek(positions[i] + 8, SeekOrigin.Begin);//定位文件指针到当日数据开头
|
|
sizes[i] = reader.ReadInt32();//sizes为当日该标签数
|
|
}
|
|
//reader.Close();
|
|
HistoryData data = HistoryData.Empty;
|
|
//stream.Read(new byte[]
|
|
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, Guid.NewGuid().ToString(), filelen, MemoryMappedFileAccess.Read,
|
|
HandleInheritability.Inheritable, false))
|
|
{
|
|
for (int k = 0; k < ln; k++)//先读入当日索引区
|
|
{
|
|
if (positions[k] < 0x100 || sizes[k] <= 0 || positions[k] + sizes[k] > filelen)
|
|
continue;
|
|
//if (sizes[k] == 0) continue;
|
|
long pos = 0;
|
|
int count = 0;
|
|
int day = 0;
|
|
int len = 0;
|
|
DataType type = DataType.NONE;
|
|
using (MemoryMappedViewAccessor acc1 = mapp.CreateViewAccessor(positions[k], 12 + sizes[k] * 11, MemoryMappedFileAccess.Read))//12是头的长度,11是一个格式字段的长度
|
|
{
|
|
day = acc1.ReadInt32(0);//当日日期部分
|
|
int index = BinarySearch(acc1, (int)sizes[k], ID);//找到当天 指定标签的记录索引
|
|
if (index >= 0)
|
|
index = index * 11 + 12;//如找到,则定位到当日数据的元数据(相对位移)
|
|
//sw.Stop();
|
|
else continue;
|
|
byte tp = acc1.ReadByte(index + 2);//读入数据类型
|
|
type = (DataType)tp;
|
|
len = dataLen[tp];//4,6,8分别为存储的标签长度,其中4字节是时间戳
|
|
count = acc1.ReadInt32(index + 3);//读入数量
|
|
pos = positions[k] + 12 + sizes[k] * 11 + acc1.ReadInt32(index + 7);//指针指向当日当前标签第一条记录
|
|
}
|
|
using (MemoryMappedViewAccessor acc2 = mapp.CreateViewAccessor(pos, count * len, MemoryMappedFileAccess.Read))//重新从头定位文件指针到数据区
|
|
{
|
|
pos = 0;
|
|
int j = 0;
|
|
if (k == 0)//判断是否为起始日期或结束日期
|
|
{
|
|
int ind = BinarySearchTime(acc2, 0, count, len, startTicks);//根据时间排序方式二分法查找当日当前时间节点的数据,如为第一日
|
|
if (ind < 0) ind = ~ind;
|
|
j += ind;
|
|
pos += ind * len;
|
|
}
|
|
if (k == ln - 1)
|
|
{
|
|
int ind = BinarySearchTime(acc2, 0, count, len, endTicks);//如果为最后一日的数据,则按结束时间定位
|
|
count = ind >= 0 ? ind : ~ind;
|
|
}
|
|
while (j++ < count)
|
|
{
|
|
data.ID = ID;
|
|
data.TimeStamp = new SqlDateTime(day, acc2.ReadInt32(pos)).Value;//日期在前(4位)
|
|
pos += 4;//数据区也是4位
|
|
switch (type)
|
|
{
|
|
case DataType.BOOL:
|
|
data.Value.Boolean = acc2.ReadBoolean(pos);
|
|
pos++;
|
|
break;
|
|
case DataType.BYTE:
|
|
data.Value.Byte = acc2.ReadByte(pos);
|
|
pos++;
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
data.Value.Int16 = acc2.ReadInt16(pos);
|
|
pos += 2;
|
|
break;
|
|
case DataType.INT:
|
|
data.Value.Int32 = acc2.ReadInt32(pos);
|
|
pos += 4;
|
|
break;
|
|
case DataType.FLOAT:
|
|
data.Value.Single = acc2.ReadSingle(pos);
|
|
pos += 4;
|
|
break;
|
|
}
|
|
yield return data;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
reader.Close();
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
public static IEnumerable<HistoryData> LoadFromDatabase(DateTime start, DateTime end, short? ID = null)
|
|
{
|
|
using (var dataReader = DataHelper.Instance.ExecuteProcedureReader("READHDATA",
|
|
DataHelper.CreateParam("@STARTTIME", SqlDbType.DateTime, start),
|
|
DataHelper.CreateParam("@ENDTIME", SqlDbType.DateTime, end),
|
|
DataHelper.CreateParam("@ID", SqlDbType.Int, (object)ID ?? DBNull.Value)))
|
|
{
|
|
if (dataReader == null) yield break;
|
|
HistoryData data = HistoryData.Empty;
|
|
int itime = ID.HasValue ? 0 : 1;
|
|
int ivalue = ID.HasValue ? 1 : 2;
|
|
int itype = ID.HasValue ? 2 : 3;
|
|
while (dataReader.Read())
|
|
{
|
|
data.ID = ID.HasValue ? ID.Value : dataReader.GetInt16(0);
|
|
data.TimeStamp = dataReader.GetDateTime(itime);
|
|
switch ((DataType)dataReader.GetByte(itype))
|
|
{
|
|
case DataType.BOOL:
|
|
data.Value.Boolean = dataReader.GetFloat(ivalue) > 0 ? true : false;
|
|
break;
|
|
case DataType.BYTE:
|
|
data.Value.Byte = Convert.ToByte(dataReader.GetFloat(ivalue));
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
data.Value.Int16 = Convert.ToInt16(dataReader.GetFloat(ivalue));
|
|
break;
|
|
case DataType.INT:
|
|
data.Value.Int32 = Convert.ToInt32(dataReader.GetFloat(ivalue));
|
|
break;
|
|
case DataType.FLOAT:
|
|
data.Value.Single = dataReader.GetFloat(ivalue);
|
|
break;
|
|
}
|
|
yield return data;
|
|
}
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
public static IEnumerable<HistoryData> LoadFromDatabaseAtTime(short? ID, params DateTime[] timeStamps)
|
|
{
|
|
StringBuilder sql = new StringBuilder("SELECT ");
|
|
if (ID == null) sql.Append("ID,");
|
|
sql.Append(" [TIMESTAMP],[VALUE],M.DATATYPE FROM LOG_HDATA L INNER JOIN META_TAG M ON L.ID=M.TAGID WHERE");
|
|
if (ID != null) sql.Append(" ID=").Append(ID.Value).Append(" AND ");
|
|
sql.Append(" [TIMESTAMP] IN(");
|
|
for (int i = 0; i < timeStamps.Length; i++)
|
|
{
|
|
sql.Append("'").Append(timeStamps[i]).Append("',");
|
|
}
|
|
using (var dataReader = DataHelper.Instance.ExecuteReader(sql.Append("1)").ToString()))
|
|
{
|
|
if (dataReader == null) yield break;
|
|
HistoryData data = HistoryData.Empty;
|
|
int itime = ID == null ? 0 : 1;
|
|
int ivalue = ID == null ? 1 : 2;
|
|
int itype = ID == null ? 2 : 3;
|
|
while (dataReader.Read())
|
|
{
|
|
data.ID = ID == null ? dataReader.GetInt16(0) : ID.Value;
|
|
data.TimeStamp = dataReader.GetDateTime(itime);
|
|
switch ((DataType)dataReader.GetByte(itype))
|
|
{
|
|
case DataType.BOOL:
|
|
data.Value.Boolean = dataReader.GetFloat(ivalue) > 0 ? true : false;
|
|
break;
|
|
case DataType.BYTE:
|
|
data.Value.Byte = Convert.ToByte(dataReader.GetFloat(ivalue));
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
data.Value.Int16 = Convert.ToInt16(dataReader.GetFloat(ivalue));
|
|
break;
|
|
case DataType.INT:
|
|
data.Value.Int32 = Convert.ToInt32(dataReader.GetFloat(ivalue));
|
|
break;
|
|
case DataType.FLOAT:
|
|
data.Value.Single = dataReader.GetFloat(ivalue);
|
|
break;
|
|
}
|
|
yield return data;
|
|
}
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
private static int BinarySearch(MemoryMappedViewAccessor acc, int length, short value)
|
|
{
|
|
int i = 0;
|
|
int num2 = length - 1;
|
|
while (i <= num2)
|
|
{
|
|
int num3 = i + ((num2 - i) >> 1);
|
|
int num4 = acc.ReadInt16(12 + num3 * 11).CompareTo(value);
|
|
if (num4 == 0)
|
|
{
|
|
return num3;
|
|
}
|
|
if (num4 < 0)
|
|
{
|
|
i = num3 + 1;
|
|
}
|
|
else
|
|
{
|
|
num2 = num3 - 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private static int BinarySearchTime(MemoryMappedViewAccessor acc, long offset, int count, int len, int ticks)
|
|
{
|
|
int i = 0;
|
|
int num2 = count - 1;
|
|
while (i <= num2)
|
|
{
|
|
int num3 = i + ((num2 - i) >> 1);
|
|
int num4 = acc.ReadInt32(offset + num3 * len).CompareTo(ticks);
|
|
if (num4 == 0)
|
|
{
|
|
return num3;
|
|
}
|
|
if (num4 < 0)
|
|
{
|
|
i = num3 + 1;
|
|
}
|
|
else
|
|
{
|
|
num2 = num3 - 1;
|
|
}
|
|
}
|
|
return ~i;
|
|
}
|
|
|
|
public static void SDTCompression(int year, int month, float E = 0.7f)
|
|
{
|
|
//Stopwatch sw = Stopwatch.StartNew();
|
|
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString());
|
|
using (FileStream stream = File.Open(path + ".bin", FileMode.Open, FileAccess.Read, FileShare.Read))
|
|
{
|
|
using (FileStream outstream = File.Create(path + ".sdt"))
|
|
{
|
|
outstream.Write(new byte[0x100], 0, 0x100);
|
|
BinaryWriter w = new BinaryWriter(outstream);
|
|
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, "map1", stream.Length,
|
|
MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, false))
|
|
{
|
|
int days = DateTime.DaysInMonth(year, month);
|
|
long[] ps = new long[days + 1];
|
|
long[] ps1 = new long[days + 1];
|
|
long[] sizes = new long[days];
|
|
MemoryMappedViewAccessor acc1 = mapp.CreateViewAccessor(0, 8 * days);
|
|
long begin = 0;
|
|
ps[0] = acc1.ReadInt64(begin);
|
|
for (int i = 0; i < days; i++)
|
|
{
|
|
begin += 8;
|
|
ps[i + 1] = (i == days - 1 ? stream.Length : acc1.ReadInt64(begin));
|
|
sizes[i] = ps[i + 1] - ps[i];
|
|
}
|
|
acc1.Dispose();
|
|
for (int i = 0; i < days; i++)
|
|
{
|
|
if (ps[i] < 0x100 || sizes[i] <= 0)
|
|
continue;
|
|
using (MemoryMappedViewAccessor acc = mapp.CreateViewAccessor(ps[i], sizes[i]))
|
|
{
|
|
ps1[i] = outstream.Position;
|
|
int len = acc.ReadInt32(8);
|
|
int len1 = len * 11 + 12;
|
|
HDataFormat[] list = new HDataFormat[len];
|
|
w.Write(acc.ReadInt32(0));
|
|
w.Write(acc.ReadInt32(4));
|
|
w.Write(len);
|
|
outstream.Write(new byte[len1 - 12], 0, len1 - 12);
|
|
long pos = 12;
|
|
int off = 0;
|
|
for (int j = 0; j < len; j++)
|
|
{
|
|
short id; byte type; int count; int offset;
|
|
id = acc.ReadInt16(pos);
|
|
type = acc.ReadByte(pos + 2);
|
|
count = acc.ReadInt32(pos + 3);
|
|
offset = acc.ReadInt32(pos + 7);
|
|
list[j].ID = id;
|
|
list[j].Type = (DataType)type;
|
|
list[j].Offset = off;//此处可采取三次到五次抽样得到E和TLM
|
|
if (count < 3)
|
|
{
|
|
long pos2 = len1 + offset;
|
|
for (int m = 0; m < count; m++)
|
|
{
|
|
w.Write(acc.ReadInt32(pos2));
|
|
pos2 += 4;
|
|
w.Write(acc.ReadSingle(pos2));
|
|
pos2 += 4;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
switch (list[j].Type)
|
|
{
|
|
case DataType.FLOAT:
|
|
{
|
|
int crt = 0; int net = 0;
|
|
float crv = 0; float nev = 0;
|
|
int maxt = 0; int mint = maxt; int sumt = 0;
|
|
float minv = 0; float maxv = minv; float sumv = 0;
|
|
int old_time = 0; int time = 0;
|
|
float mem = 0; float old_mem = 0;
|
|
long pp = len1 + offset;
|
|
long pos2 = pp + 16;
|
|
for (int c = 0; c < 9; c++)
|
|
{
|
|
crt = acc.ReadInt32(pp);
|
|
pp += 4;
|
|
crv = acc.ReadSingle(pp);
|
|
pp += 4;
|
|
if (c > 0)
|
|
{
|
|
float cv = crv - nev;
|
|
int ct = crt - net;
|
|
if (c == 1)
|
|
{
|
|
time = crt;
|
|
mem = crv;
|
|
maxt = mint = ct;
|
|
minv = maxv = cv;
|
|
}
|
|
else
|
|
{
|
|
if (cv > maxv)
|
|
maxv = cv;
|
|
if (cv < minv)
|
|
minv = cv;
|
|
if (ct > maxt)
|
|
maxt = ct;
|
|
if (ct < mint)
|
|
mint = ct;
|
|
}
|
|
sumv += cv;
|
|
sumt += ct;
|
|
}
|
|
else
|
|
{
|
|
old_mem = crv;
|
|
old_time = crt;
|
|
}
|
|
nev = crv;
|
|
net = crt;
|
|
}
|
|
int TLM = (sumt - maxt - mint) / 2;
|
|
float E1 = E * (sumv - maxv - minv) / 6;
|
|
int sum = 1;
|
|
//old_time = now_time = new_time = 0;
|
|
float timespan;
|
|
w.Write(old_time);
|
|
w.Write(old_mem);
|
|
float k1, k2, k;
|
|
timespan = time - old_time;
|
|
k = (mem - old_mem) / timespan;
|
|
k1 = k + (E1 / timespan);
|
|
k2 = 2 * k - k1;
|
|
for (int m = 2; m < count; m++)
|
|
{
|
|
if (timespan >= TLM || k < k2 || k > k1)
|
|
{
|
|
++sum;
|
|
w.Write(old_time);
|
|
w.Write(old_mem);
|
|
k1 = k + (E1 / timespan);
|
|
k2 = 2 * k - k1;
|
|
}
|
|
old_time = time;
|
|
old_mem = mem;
|
|
time = acc.ReadInt32(pos2);
|
|
pos2 += 4;
|
|
mem = acc.ReadSingle(pos2);
|
|
pos2 += 4;
|
|
timespan = time - old_time;
|
|
k = (mem - old_mem) / timespan;
|
|
}
|
|
list[j].Count = sum;
|
|
off += sum * 8;
|
|
}
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
{
|
|
int crt = 0; int net = 0;
|
|
short crv = 0; short nev = 0;
|
|
int maxt = 0; int mint = maxt; int sumt = 0;
|
|
int minv = 0; int maxv = minv; int sumv = 0;
|
|
int old_time = 0; int time = 0;
|
|
short mem = 0; short old_mem = 0;
|
|
long pp = len1 + offset;
|
|
long pos2 = pp + 12;
|
|
for (int c = 0; c < 9; c++)
|
|
{
|
|
crt = acc.ReadInt32(pp);
|
|
pp += 4;
|
|
crv = acc.ReadInt16(pp);
|
|
pp += 2;
|
|
if (c > 0)
|
|
{
|
|
int cv = crv - nev;
|
|
int ct = crt - net;
|
|
if (c == 1)
|
|
{
|
|
time = crt;
|
|
maxt = mint = ct;
|
|
mem = crv;
|
|
minv = maxv = cv;
|
|
}
|
|
else
|
|
{
|
|
if (cv > maxv)
|
|
maxv = cv;
|
|
if (cv < minv)
|
|
minv = cv;
|
|
if (ct > maxt)
|
|
maxt = ct;
|
|
if (ct < mint)
|
|
mint = ct;
|
|
}
|
|
sumv += cv;
|
|
sumt += ct;
|
|
}
|
|
else
|
|
{
|
|
old_mem = crv;
|
|
old_time = crt;
|
|
}
|
|
nev = crv;
|
|
net = crt;
|
|
}
|
|
int TLM = (sumt - maxt - mint) / 2;
|
|
float E1 = E * (sumv - maxv - minv) / 6;
|
|
int sum = 1;
|
|
float timespan;
|
|
w.Write(old_time);
|
|
w.Write(old_mem);
|
|
float k1, k2, k;
|
|
timespan = time - old_time;
|
|
k = (mem - old_mem) / timespan;
|
|
k1 = k + (E1 / timespan);
|
|
k2 = 2 * k - k1;
|
|
for (int m = 2; m < count; m++)
|
|
{
|
|
if (timespan >= TLM || k < k2 || k > k1)
|
|
{
|
|
++sum;
|
|
w.Write(old_time);
|
|
w.Write(old_mem);
|
|
k1 = k + (E1 / timespan);
|
|
k2 = 2 * k - k1;
|
|
}
|
|
old_time = time;
|
|
old_mem = mem;
|
|
time = acc.ReadInt32(pos2);
|
|
pos2 += 4;
|
|
mem = acc.ReadInt16(pos2);
|
|
pos2 += 2;
|
|
timespan = time - old_time;
|
|
k = (mem - old_mem) / timespan;
|
|
}
|
|
|
|
list[j].Count = sum;
|
|
off += sum * 8;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
byte[] buffer = new byte[count * dataLen[type]];
|
|
stream.Seek(ps[i] + len1 + offset, SeekOrigin.Begin);
|
|
stream.Read(buffer, 0, buffer.Length);
|
|
outstream.Write(buffer, 0, buffer.Length);
|
|
list[j].Count = count;
|
|
off += buffer.Length;
|
|
}
|
|
break;
|
|
}
|
|
pos += 11;
|
|
}
|
|
}
|
|
outstream.Seek(ps1[i] + 12, SeekOrigin.Begin);
|
|
for (int j = 0; j < len; j++)
|
|
{
|
|
w.Write(list[j].ID);
|
|
w.Write((byte)list[j].Type);
|
|
w.Write(list[j].Count);
|
|
w.Write(list[j].Offset);
|
|
}
|
|
ps1[i + 1] = outstream.Seek(0, SeekOrigin.End);
|
|
}
|
|
|
|
}
|
|
outstream.Seek(0, SeekOrigin.Begin);
|
|
for (int i = 0; i < days + 1; i++)
|
|
{
|
|
w.Write(ps1[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//遍历两个文件夹下所有历史记录文件;如日期无重复,则复制源路径下文件到目标路径;否则合并到一个文件
|
|
public static unsafe bool Merge(string sourcePath, string targetPath)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public static unsafe ushort ToFloat16(float f)
|
|
{
|
|
uint* i = (uint*)&f;
|
|
uint sign = (*i >> 31) & 0x1;
|
|
uint exponent = ((*i >> 23) & 0xff) - 0x7f;
|
|
uint mantissa = (*i) & 0x7fffff;
|
|
|
|
exponent += 0x7;
|
|
uint ret = ((sign & 0x1) << 15);
|
|
ret |= ((exponent & 0xf) << 11);
|
|
ret |= ((mantissa >> 13) & 0x7ff);
|
|
return (ushort)ret;
|
|
}
|
|
|
|
public static unsafe float ToFloat32(ushort f)
|
|
{
|
|
ushort* i = (ushort*)&f;
|
|
int sign = (*i >> 15) & 0x1;
|
|
int exponent = ((*i >> 11) & 0xf) - 0x7;
|
|
int mantissa = (*i) & 0x7ff;
|
|
|
|
exponent += 0x7f;
|
|
int ret = ((sign & 0x1) << 31);
|
|
ret |= (exponent & 0xff) << 23;
|
|
ret |= (mantissa << 13) & 0x7fffff;
|
|
return *((float*)&ret);
|
|
}
|
|
|
|
|
|
public static float[] Interpolation(float[] dataIn, int n)
|
|
{
|
|
float[] dataOut = new float[n];
|
|
int lenIn = dataIn.Length;
|
|
float[] divOut = new float[n];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
divOut[i] = divOut[i - 1] + lenIn / (float)n;
|
|
}
|
|
int k = 0;
|
|
for (int i = k; i < n; i++)
|
|
{
|
|
for (int j = 0; j < lenIn - 1; j++)
|
|
{
|
|
if (divOut[i] >= j && divOut[i] < j + 1)
|
|
{
|
|
dataOut[i] = (dataIn[j + 1] - dataIn[j]) * (divOut[i] - j) + dataIn[j];
|
|
k = i;
|
|
}
|
|
}
|
|
}
|
|
return dataOut;
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
internal struct HDataFormat
|
|
{
|
|
public short ID;
|
|
public DataType Type;
|
|
public int Count;
|
|
public int Offset;
|
|
|
|
public HDataFormat(short id, DataType type, int count, int offset)
|
|
{
|
|
ID = id;
|
|
Type = type;
|
|
Count = count;
|
|
Offset = offset;
|
|
}
|
|
}
|
|
}
|