初始化

This commit is contained in:
2025-12-11 14:04:39 +08:00
commit 1f65bbf628
2676 changed files with 838983 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using CSRedis;
using LogCap.Common;
using Microsoft.Extensions.Configuration;
namespace Common
{
/// <summary>
/// Redis缓存辅助类
/// </summary>
public class CSRedisCacheHelper
{
public static CSRedisClient? redis = null;
public static CSRedisClient? redis1 = null;
public static CSRedisClient? redis2 = null;
public static CSRedisClient? redis3 = null;
public static CSRedisClient? redis4 = null;
public static CSRedisClient? redis5 = null;
public static int SessionExpireMinutes = 5;
public static IConfiguration Configuration { get; set; }
static CSRedisCacheHelper()
{
var redisHostStr = ReadConfig.Instance.get_redis_server_session;
var get_redis_auth = ReadConfig.Instance.get_redis_auth;
if (!string.IsNullOrEmpty(redisHostStr))
{
redis = new CSRedisClient(redisHostStr + string.Format(",password={0},defaultDatabase=0", get_redis_auth));
redis1 = new CSRedisClient(redisHostStr + string.Format(",password={0},defaultDatabase=1", get_redis_auth));
redis2 = new CSRedisClient(redisHostStr + string.Format(",password={0},defaultDatabase=2", get_redis_auth));
redis3 = new CSRedisClient(redisHostStr + string.Format(",password={0},defaultDatabase=3", get_redis_auth));
redis4 = new CSRedisClient(redisHostStr + string.Format(",password={0},defaultDatabase=4", get_redis_auth));
redis5 = new CSRedisClient(redisHostStr + string.Format(",password={0},defaultDatabase=5", get_redis_auth));
}
}
/// <summary>
/// 添加缓存
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="value"></param>
public static void Set<T>(string key, T value)
{
redis.Set(key, value, SessionExpireMinutes * 60);
}
public static T Get<T>(string key)
{
return redis.Get<T>(key);
}
public static void Del(string[] key)
{
redis.Del(key.ToArray());
}
public static T ForeverGet<T>(string key)
{
return redis1.Get<T>(key);
}
public static void Forever<T>(string key, T value)
{
redis1.Set(key, value, 0);
}
public static T Get_Partition<T>(string key, int SliceNo = 2)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
return client.Get<T>(key);
}
public static void Set_Partition<T>(string key, T value, int SliceNo = 2, int ExpireMinutes = 0)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
client.Set(key, value, ExpireMinutes * 60);
}
public static void Del_Partition(string[] key, int SliceNo = 2)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
client.Del(key);
}
public static bool Contains_Partition(string key, int SliceNo = 2)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
bool result = client.Exists(key);
return result;
}
private static CSRedisClient WhitchRedisSlice(int SliceNo)
{
CSRedisClient client = null;
if (SliceNo == 1)
{
client = redis1;
}
else if (SliceNo == 2)
{
client = redis2;
}
else
{
client = redis;
}
return client;
}
public static long GetForever_ExpireMinutes<T>(string key)
{
return redis.Ttl(key);
}
/// <summary>
/// 获取
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static T Get<T>(string key, string mac)
{
T obj = redis.Get<T>(mac);
if (obj == null)
{
return redis.Get<T>(key);
}
return obj;
}
/// <summary>
/// 判断是否存在
/// </summary>
/// <param name="key"></param>
/// <param name="mac"></param>
/// <returns></returns>
public static bool Contains(string key, string mac)
{
bool result = redis.Exists(mac);
if (!result)
{
result = redis.Exists(key);
}
return result;
}
public static bool Contains(string key)
{
bool result = redis.Exists(key);
return result;
}
public static void Publish(string Topic, string Payload)
{
redis.PublishAsync(Topic,Payload);
}
/// <summary>
/// 添加Hash缓存
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="value"></param>
public static void HMSet(int SliceNo, string key, params object[] value)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
client.HMSet(key, value);
}
public static void HMSet(int SliceNo, int ExpireTime_M, string key, params object[] value)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
client.HMSet(key, value);
client.Expire(key, new TimeSpan(0, ExpireTime_M, 0));
}
/// <summary>
/// 获取Hash缓存
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static T[] HMGet<T>(int SliceNo, string key, string KeyV)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
return client.HMGet<T>(key, KeyV);
}
public static Dictionary<string, string> HMGetAll(int SliceNo, string key)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
return client.HGetAll(key);
}
/// <summary>
/// 删除Hash缓存
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static long HDelAll<T>(int SliceNo, string key)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
return client.HDel(key);
}
public static long HDel(int SliceNo, string key, string key1)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
return client.HDel(key, key1);
}
public static void ListAdd(int SliceNo, string key, params object[] obj)
{
CSRedisClient client = WhitchRedisSlice(SliceNo);
client.LPush(key, obj);
}
public static void GenericSerNo(string key)
{
redis1.IncrBy(key, 1);
}
}
}

105
Common/Cache/Cache.cs Normal file
View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommonEntity.CacheEntity;
using FASTER.core;
namespace Common.Cache
{
public class Cache
{
private static System.Timers.Timer _checkpointTimer;
public static FasterKVSettings<string, MyData_Cache> settings { get; set; }
public static FasterKV<string, MyData_Cache> store { get; set; }
public static ConcurrentBag<ClientSession<string, MyData_Cache, MyData_Cache, MyData_Cache, Empty, IFunctions<string, MyData_Cache, MyData_Cache, MyData_Cache, Empty>>> _sessions = new();
static Cache()
{
settings = new FasterKVSettings<string, MyData_Cache>()
{
LogDevice = Devices.CreateLogDevice("c:/youtemp/log"), // 元数据日志
ObjectLogDevice = Devices.CreateLogDevice("c:/youtemp/obj"), // 对象数据日志
TryRecoverLatest = true,
ValueSerializer = () => new MyValueSerializer<MyData_Cache>()
};
store = new FasterKV<string, MyData_Cache>(settings);
var session1 = store.NewSession(new SimpleFunctions<string, MyData_Cache>());
var session2 = store.NewSession(new SimpleFunctions<string, MyData_Cache>());
var session3 = store.NewSession(new SimpleFunctions<string, MyData_Cache>());
var session4 = store.NewSession(new SimpleFunctions<string, MyData_Cache>());
var session5 = store.NewSession(new SimpleFunctions<string, MyData_Cache>());
_sessions.Add(session1);
_sessions.Add(session2);
_sessions.Add(session3);
_sessions.Add(session4);
_sessions.Add(session5);
_checkpointTimer = new System.Timers.Timer();
_checkpointTimer.Interval = 60000; // 1分钟做一次检查点
_checkpointTimer.Elapsed += _checkpointTimer_Elapsed;
_checkpointTimer.Start();
}
private async static void _checkpointTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
try
{
if (_sessions != null)
{
await store.TakeFullCheckpointAsync(CheckpointType.FoldOver);
}
}
catch (Exception)
{
}
}
public static async Task SetCache(string Key, MyData_Cache ValueData)
{
bool isexits = _sessions.TryTake(result: out var TakeSession);
if (isexits)
{
TakeSession.Upsert(ref Key, ref ValueData);
_sessions.Add(TakeSession);
await TakeSession.CompletePendingAsync(true);
}
else
{
var NewSession = store.NewSession(new SimpleFunctions<string, MyData_Cache>());
var status = NewSession.Upsert(ref Key, ref ValueData);
_sessions.Add(NewSession);
}
//await store.TakeFullCheckpointAsync(CheckpointType.FoldOver);
}
public static async Task<MyData_Cache> GetCache(string Key)
{
MyData_Cache output = null;
bool isexits = _sessions.TryTake(result: out var TakeSession);
if (isexits)
{
_sessions.Add(TakeSession);
TakeSession.Refresh();
var status = await TakeSession.ReadAsync(ref Key, ref output);
MyData_Cache output1 = status.Output;
output = output1;
}
else
{
var NewSession = store.NewSession(new SimpleFunctions<string, MyData_Cache>());
_sessions.Add(NewSession);
var status = await NewSession.ReadAsync(ref Key, ref output);
MyData_Cache output1 = status.Output;
output = output1;
}
return output;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using FASTER.core;
using System.Xml.Linq;
using CommonEntity.CacheEntity;
using MemoryPack;
namespace Common.Cache
{
public class MyValueSerializer<T> : BinaryObjectSerializer<T>
{
public override void Serialize(ref T value)
{
//writer.Write(IPAddress.HostToNetworkOrder(Q.Length)); // 4字节长度头
var Q = MemoryPackSerializer.Serialize(value);
writer.Write(Q.Length); // 4字节长度头
writer.Write(Q);
}
public override void Deserialize(out T value)
{
int length = reader.ReadInt32(); // 读长度头
byte[] buffer = reader.ReadBytes(length); // 读数据体
value = MemoryPackSerializer.Deserialize<T>(buffer); // 反序列化
}
}
}

23
Common/CacheKey.cs Normal file
View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogCap.Common
{
public class CacheKey
{
public static string RoomIP_Port_Prefix = "Log_IP_Port";
public static string Key = "monitor_host";
public static string MonitorLogPrefix = "AllLogKey_";
public static string MQTTValidatePrefix = "MQTTValidatePrefix_";
public static string DeviceInfo = "DeviceInfo";
}
}

24
Common/Common.csproj Normal file
View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CSRedisCore" Version="3.8.804" />
<PackageReference Include="MessagePack" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.21" />
<PackageReference Include="Microsoft.FASTER.Core" Version="2.6.5" />
<PackageReference Include="RestSharp" Version="112.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommonEntity\CommonEntity.csproj" />
</ItemGroup>
</Project>

24
Common/DeviceConvert.cs Normal file
View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class DeviceConvert
{
public static string Convert_To_Address(byte[] deviceAddress)
{
if (deviceAddress[0] == 0x00)//指令场景地址
{
return String.Format("{0}.{1}.{2}", deviceAddress[1], deviceAddress[2], deviceAddress[3]);
}
else
{
ushort type = BitConverter.ToUInt16(deviceAddress, 2);
return String.Format("{0:000}{1:000}{2:000}", deviceAddress[0], deviceAddress[1], type);
}
}
}
}

33
Common/Grafana.cs Normal file
View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class Grafana
{
public static string WaiBuJianKongSourceFileName = "Para_copy.yaml";
public static string WaiBuJianKongTargetFileName = "C:\\Program Files\\windows_exporter\\textfile_inputs\\Para.prom";
public static string Data = "";
//static Grafana()
//{
// string getdata = File.ReadAllText(WaiBuJianKongSourceFileName, Encoding.UTF8);
// Data = getdata;
//}
public static grafana_data data = new grafana_data();
}
public class grafana_data
{
public string? device_id { get; set; } = "0";
public string? device_status { get; set; } = "0";
public string? realtime_value { get; set; }= "0";
public string? environment_value { get; set; }="0";
public string? mcu_temperature { get; set; } = "0";
public string? sensor_temperature { get; set; }= "0";
public string? sensor_humidity { get; set; } = "0";
public string? adc_raw_value { get; set; } = "0";
}
}

45
Common/HttpSend.cs Normal file
View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using RestSharp;
namespace LogCap.Common
{
public class HttpSend
{
public static readonly string debug_log_report_url = ReadConfig.Instance.get_debug_log_report_url;
public static readonly string debug_log_report_mqtt_topic = ReadConfig.Instance.get_debug_log_report_mqtt_topic;
public static RestClient client1 = new RestClient(debug_log_report_url);
public static async void SendLog(string MyCommandType, long hotel_code, int host_id, string roomnumber, string hostnumber, string WWW_IP, int WWW_Port, string lan_ip, int lan_port, string mac, string send_or_receive, string dataHex)
{
try
{
string ti = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
string mm = "";
string str = Newtonsoft.Json.JsonConvert.SerializeObject(mm);
//Console.WriteLine("发送MQTT的数据为" + str);
var request1 = new RestRequest("/", Method.Post);
//注意方法是POST
//两个参数名字必须是 topic 和payload ,区分大小写的
//Console.WriteLine("Topic: " + debug_log_report_mqtt_topic);
request1.AddParameter("topic", debug_log_report_mqtt_topic);
request1.AddParameter("payload", str);
await HttpSend.client1.ExecuteAsync(request1);
Console.WriteLine("当前时间为:" + ti+" Data: "+dataHex);
}
catch (Exception ex)
{
Console.WriteLine("发送MQTT数据出错" + ex.Message);
}
}
}
}

93
Common/Id_G.cs Normal file
View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class IdWorker
{
//起始的时间戳
private static long START_STMP = 1480166465631L;
//每一部分占用的位数
private static int SEQUENCE_BIT = 12; //序列号占用的位数
private static int MACHINE_BIT = 5; //机器标识占用的位数
private static int DATACENTER_BIT = 5;//数据中心占用的位数
//每一部分的最大值
private static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
//每一部分向左的位移
private static int MACHINE_LEFT = SEQUENCE_BIT;
private static int DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private static int TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId = 1; //数据中心
private long machineId = 1; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
#region :
private static readonly Lazy<IdWorker> lazy = new Lazy<IdWorker>(() => new IdWorker());
public static IdWorker Singleton { get { return lazy.Value; } }
private IdWorker() { }
#endregion
public IdWorker(long cid, long mid)
{
if (cid > MAX_DATACENTER_NUM || cid < 0) throw new Exception($"中心Id应在(0,{MAX_DATACENTER_NUM})之间");
if (mid > MAX_MACHINE_NUM || mid < 0) throw new Exception($"机器Id应在(0,{MAX_MACHINE_NUM})之间");
datacenterId = cid;
machineId = mid;
}
/// <summary>
/// 产生下一个ID
/// </summary>
/// <returns></returns>
public long nextId()
{
long currStmp = getNewstmp();
if (currStmp < lastStmp) throw new Exception("时钟倒退Id生成失败");
if (currStmp == lastStmp)
{
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) currStmp = getNextMill();
}
else
{
//不同毫秒内序列号置为0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| datacenterId << DATACENTER_LEFT //数据中心部分
| machineId << MACHINE_LEFT //机器标识部分
| sequence; //序列号部分
}
private long getNextMill()
{
long mill = getNewstmp();
while (mill <= lastStmp)
{
mill = getNewstmp();
}
return mill;
}
private long getNewstmp()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
}
}

44
Common/MyMessagePacker.cs Normal file
View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MessagePack;
namespace CommonTools
{
public class MyMessagePacker
{
public static byte[] FastSerialize<T>(T t)
{
byte[] bytes = MessagePackSerializer.Serialize(t);
return bytes;
}
public static T FastDeserialize<T>(byte[] bytes)
{
T mc2 = MessagePackSerializer.Deserialize<T>(bytes);
return mc2;
}
}
[MessagePackObject]
public class MyClass
{
// Key attributes take a serialization index (or string name)
// The values must be unique and versioning has to be considered as well.
// Keys are described in later sections in more detail.
[Key(0)]
public int Age { get; set; }
[Key(1)]
public string FirstName { get; set; }
[Key(2)]
public string LastName { get; set; }
// All fields or properties that should not be serialized must be annotated with [IgnoreMember].
[IgnoreMember]
public string FullName { get { return FirstName + LastName; } }
}
}

69
Common/ReadConfig.cs Normal file
View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
namespace LogCap.Common
{
public class ReadConfig
{
public static ReadConfig Instance = new ReadConfig();
public IConfiguration? Configuration { get; set; }
public string? MySQLConnectionString { get; set; }
public string? get_monitor_filter { get; set; }
public string? get_session_expire_minutes { get; set; }
public string? get_redis_server_session { get; set; }
public string? get_redis_auth { get; set; }
public string? get_redis_max_read_pool { get; set; }
public string? get_redis_max_write_pool { get; set; }
public string? get_debug_log_report_url { get; set; }
public string? get_debug_log_report_mqtt_topic { get; set; }
public string? monitor_log_expire_minutes { get; set; }
public string? monitor_server_ip { get; set; }
public int monitor_server_port { get; set; }
public string? CRICS_URL { get; set; }
public string? MQTT_ServerIP { get; set; }
public int? MQTT_ServerPort { get; set; }
public string? MQTT_User { get; set; }
public string? MQTT_PassWord { get; set; }
public ReadConfig()
{
Configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
MySQLConnectionString = Configuration.GetConnectionString("SQLServerConnectionString");
get_monitor_filter = Configuration.GetSection("monitor_filter").Value;
get_session_expire_minutes = Configuration.GetSection("session_expire_minutes").Value;
get_redis_server_session = Configuration.GetSection("redis_server_session").Value;
get_redis_auth = Configuration.GetSection("get_redis_auth").Value;
get_redis_max_read_pool = Configuration.GetSection("redis_max_read_pool").Value;
get_redis_max_write_pool = Configuration.GetSection("redis_max_write_pool").Value;
monitor_log_expire_minutes = Configuration.GetSection("monitor_log_expire_minutes").Value;
get_debug_log_report_url = Configuration.GetSection("debug_log_report_url").Value;
get_debug_log_report_mqtt_topic = Configuration.GetSection("debug_log_report_mqtt_topic").Value;
monitor_server_ip = Configuration.GetSection("monitor_server_ip").Value;
int i = 3339;
int.TryParse(Configuration.GetSection("monitor_server_port").Value, out i);
monitor_server_port = i;
MQTT_ServerIP = Configuration.GetSection("MQTT_ServerIP").Value;
int p = 1883;
int.TryParse(Configuration.GetSection("MQTT_ServerPort").Value, out p);
MQTT_ServerPort = p;
CRICS_URL = Configuration.GetSection("CRICS_URL").Value;
MQTT_User = Configuration.GetSection("MQTT_User").Value;
MQTT_PassWord = Configuration.GetSection("MQTT_PWD").Value;
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
namespace Snowflake
{
public class DisposableAction : IDisposable
{
readonly Action _action;
public DisposableAction(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
_action = action;
}
public void Dispose()
{
_action();
}
}
}

View File

@@ -0,0 +1,118 @@
/** Copyright 2010-2012 Twitter, Inc.*/
/**
* An object that generates IDs.
* This is broken into a separate class in case
* we ever want to support multiple worker threads
* per process
*/
using System;
namespace Snowflake
{
public class IdWorker
{
public const long Twepoch = 1288834974657L;
const int WorkerIdBits = 5;
const int DatacenterIdBits = 5;
const int SequenceBits = 12;
const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
private const int WorkerIdShift = SequenceBits;
private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
private const long SequenceMask = -1L ^ (-1L << SequenceBits);
private long _sequence = 0L;
private long _lastTimestamp = -1L;
public IdWorker(long workerId, long datacenterId, long sequence = 0L)
{
WorkerId = workerId;
DatacenterId = datacenterId;
_sequence = sequence;
// sanity check for workerId
if (workerId > MaxWorkerId || workerId < 0)
{
throw new ArgumentException( String.Format("worker Id can't be greater than {0} or less than 0", MaxWorkerId) );
}
if (datacenterId > MaxDatacenterId || datacenterId < 0)
{
throw new ArgumentException( String.Format("datacenter Id can't be greater than {0} or less than 0", MaxDatacenterId));
}
//log.info(
// String.Format("worker starting. timestamp left shift {0}, datacenter id bits {1}, worker id bits {2}, sequence bits {3}, workerid {4}",
// TimestampLeftShift, DatacenterIdBits, WorkerIdBits, SequenceBits, workerId)
// );
}
public long WorkerId {get; protected set;}
public long DatacenterId {get; protected set;}
public long Sequence
{
get { return _sequence; }
internal set { _sequence = value; }
}
// def get_timestamp() = System.currentTimeMillis
readonly object _lock = new Object();
public virtual long NextId()
{
lock(_lock)
{
var timestamp = TimeGen();
if (timestamp < _lastTimestamp)
{
//exceptionCounter.incr(1);
//log.Error("clock is moving backwards. Rejecting requests until %d.", _lastTimestamp);
throw new InvalidSystemClock(String.Format(
"Clock moved backwards. Refusing to generate id for {0} milliseconds", _lastTimestamp - timestamp));
}
if (_lastTimestamp == timestamp)
{
_sequence = (_sequence + 1) & SequenceMask;
if (_sequence == 0)
{
timestamp = TilNextMillis(_lastTimestamp);
}
} else {
_sequence = 0;
}
_lastTimestamp = timestamp;
var id = ((timestamp - Twepoch) << TimestampLeftShift) |
(DatacenterId << DatacenterIdShift) |
(WorkerId << WorkerIdShift) | _sequence;
return id;
}
}
protected virtual long TilNextMillis(long lastTimestamp)
{
var timestamp = TimeGen();
while (timestamp <= lastTimestamp)
{
timestamp = TimeGen();
}
return timestamp;
}
protected virtual long TimeGen()
{
return System.CurrentTimeMillis();
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Snowflake
{
public class InvalidSystemClock : Exception
{
public InvalidSystemClock(string message) : base(message) { }
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Snowflake
{
public static class System
{
public static Func<long> currentTimeFunc = InternalCurrentTimeMillis;
public static long CurrentTimeMillis()
{
return currentTimeFunc();
}
public static IDisposable StubCurrentTime(Func<long> func)
{
currentTimeFunc = func;
return new DisposableAction(() =>
{
currentTimeFunc = InternalCurrentTimeMillis;
});
}
public static IDisposable StubCurrentTime(long millis)
{
currentTimeFunc = () => millis;
return new DisposableAction(() =>
{
currentTimeFunc = InternalCurrentTimeMillis;
});
}
private static readonly DateTime Jan1st1970 = new DateTime
(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static long InternalCurrentTimeMillis()
{
return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}
}
}