using Autofac;
|
using Bro.Common.Base;
|
using Bro.Common.Factory;
|
using Bro.Common.Helper;
|
using Bro.Common.Interface;
|
using Bro.Common.Model;
|
using Bro.Common.Model.Authority;
|
using HalconDotNet;
|
using Newtonsoft.Json;
|
using System;
|
using System.Collections.Generic;
|
using System.Collections.ObjectModel;
|
using System.Collections.Specialized;
|
using System.Diagnostics;
|
using System.Drawing;
|
using System.IO;
|
using System.Linq;
|
using System.Reflection;
|
using System.Runtime.CompilerServices;
|
using System.Threading;
|
using System.Threading.Tasks;
|
using static Bro.Common.Helper.EnumHelper;
|
|
namespace Bro.Process
|
{
|
[Process("", DeviceAttributeType.Device)]
|
public partial class ProcessControl : IProcess
|
{
|
#region AutoFac
|
private void AutoFacRegister()
|
{
|
#region AutoFac注册
|
GlobalVar.Builder.RegisterInstance(this).As<IProcess>().ExternallyOwned();
|
GlobalVar.Builder.RegisterInstance(IConfig).As<IProcessConfig>().ExternallyOwned();
|
GlobalVar.Builder.RegisterInstance(DeviceCollection);
|
GlobalVar.Builder.RegisterInstance(ProcessMethodCollection);
|
|
//if (isBuild)
|
//{
|
// GlobalVar.Container = GlobalVar.Builder.Build();
|
//}
|
#endregion
|
}
|
#endregion
|
|
#region Event
|
public event Action<string, Bitmap, string> OnBitmapOutput;
|
//public event Action<string, object> OnObjectOutput;
|
public Action<DateTime, Exception> OnExceptionOccured { get; set; }
|
public event Action<DeviceState> OnProcessStateChanged;
|
public event Action<DateTime, string, string> OnLog;
|
public event Action<string> OnAlarmUpdate;
|
#endregion
|
|
#region Property & Field
|
private IProcessConfig iConfig;
|
public IProcessConfig IConfig
|
{
|
get => iConfig;
|
set
|
{
|
iConfig = value;
|
|
LoggerHelper.LogPath = iConfig.LogPath;
|
}
|
}
|
|
|
private DeviceState processState = DeviceState.DSUninit;
|
public DeviceState ProcessState
|
{
|
get => processState;
|
set
|
{
|
if (processState != value)
|
{
|
processState = value;
|
Task.Run(() =>
|
{
|
OnProcessStateChanged?.Invoke(value);
|
});
|
}
|
}
|
}
|
|
public List<IDevice> DeviceCollection { get; set; } = new List<IDevice>();
|
|
public bool IsServiceMode { get; set; }
|
#endregion
|
|
#region 构造函数
|
public ProcessControl()
|
{
|
}
|
|
public ProcessControl(string productionCode)
|
{
|
if (productionCode.ToLower() == "default")
|
{
|
productionCode = "";
|
}
|
|
ProductionCode = productionCode;
|
}
|
#endregion
|
|
#region Initial
|
|
public void InitialProcess()
|
{
|
InitialProcess("");
|
}
|
public void InitialProcess(string configPath)
|
{
|
//ProcessException.OnExceptionNotice = LogAsync;
|
|
IConfig = LoadStationConfig(configPath);
|
|
#region 个别配置的特别处理
|
#endregion
|
|
_warningRemains.CollectionChanged -= WarningRemains_CollectionChanged;
|
_warningRemains.CollectionChanged += WarningRemains_CollectionChanged;
|
|
DeviceCollection.Clear();
|
InitialDevices();
|
InitialProcessMethods();
|
AutoFacRegister();
|
|
LogAsync(DateTime.Now, "Process Initialized", "");
|
}
|
|
private void InitialDevices()
|
{
|
IConfig.GetAllDeviceInitialConfigs().ForEach(iConfig =>
|
{
|
if (string.IsNullOrWhiteSpace(iConfig.DriverType))
|
{
|
//throw new ProcessException($"{iConfig.Name}未配置驱动类型");
|
LogAsync(DateTime.Now, $"{iConfig.Name}未配置驱动类型", "");
|
return;
|
}
|
|
IDevice device = DeviceFactory.GetDeviceInstanceByTypeName(iConfig.DriverType);
|
|
device.InitialConfig = iConfig;
|
|
RegisterDeviceEvent(device);
|
|
if (!DeviceCollection.Any(u => u.Id == device.Id))
|
{
|
DeviceCollection.Add(device);
|
}
|
});
|
}
|
|
/// <summary>
|
/// 注册设备事件
|
/// </summary>
|
/// <param name="device"></param>
|
protected virtual void RegisterDeviceEvent(IDevice device)
|
{
|
//if (device is CameraBase)
|
//{
|
// CameraBase camera = device as CameraBase;
|
// camera.OnImageUpdated -= CameraUpdateImage;
|
// camera.OnImageUpdated += CameraUpdateImage;
|
//}
|
|
if (device is IMonitor)
|
{
|
IMonitor monitor = device as IMonitor;
|
monitor.OnMonitorAlarm -= OnMonitorAlarm;
|
monitor.OnMonitorInvoke -= OnMonitorInvoke;
|
|
monitor.OnMonitorAlarm += OnMonitorAlarm;
|
monitor.OnMonitorInvoke += OnMonitorInvoke;
|
}
|
}
|
#endregion
|
|
#region Open
|
public virtual void Open()
|
{
|
Open(false);
|
}
|
|
[ProcessExceptionAspect]
|
public virtual void Open(bool _isServiceMode = false)
|
{
|
IsServiceMode = _isServiceMode;
|
|
if (ProcessState == DeviceState.DSOpen)
|
return;
|
|
OpenDevices(DeviceCollection);
|
|
ProcessState = DeviceState.DSOpen;
|
|
LogAsync(DateTime.Now, "Process Opened", "");
|
}
|
|
private void OpenDevices<T>(List<T> devices) where T : IDevice
|
{
|
devices.ForEach(d =>
|
{
|
if (d.InitialConfig?.IsEnabled ?? false)
|
{
|
d.OnLog -= OnDeviceLog;
|
d.OnLog += OnDeviceLog;
|
|
d.StateChange(DeviceState.DSInit);
|
d.StateChange(DeviceState.DSOpen);
|
}
|
});
|
}
|
#endregion
|
|
#region Close
|
[ProcessExceptionAspect]
|
public virtual void Close()
|
{
|
if (ProcessState == DeviceState.DSClose)
|
return;
|
|
CloseDevice(DeviceCollection);
|
|
ProcessState = DeviceState.DSClose;
|
|
LogAsync(DateTime.Now, "Process Closed", "");
|
}
|
|
private void CloseDevice<T>(List<T> devices) where T : IDevice
|
{
|
devices.ForEach(d =>
|
{
|
if (d.CurrentState != DeviceState.DSClose)
|
{
|
d.StateChange(DeviceState.DSClose);
|
}
|
});
|
}
|
#endregion
|
|
#region 配置操作和产品代码
|
string _configBackupStr = "";
|
string _configPath = "";
|
private string productionCode = null;
|
public string ProductionCode
|
{
|
get => productionCode;
|
set
|
{
|
if (productionCode != value && value != null)
|
{
|
productionCode = value;
|
|
string baseDir = SettingHelper.GetConfigFilePath();
|
|
if (string.IsNullOrWhiteSpace(baseDir))
|
{
|
baseDir = AppDomain.CurrentDomain.BaseDirectory;
|
}
|
|
if (!string.IsNullOrWhiteSpace(productionCode))
|
{
|
_configPath = Path.Combine(baseDir, $"Config_{productionCode}.json");
|
}
|
else
|
{
|
_configPath = Path.Combine(baseDir, "Config.json");
|
}
|
}
|
}
|
}
|
|
private IProcessConfig LoadStationConfig(string configPath = "")
|
{
|
IProcessConfig config = null;
|
|
if (string.IsNullOrWhiteSpace(configPath))
|
{
|
configPath = _configPath;
|
}
|
|
if (File.Exists(configPath))
|
{
|
using (StreamReader reader = new StreamReader(configPath, System.Text.Encoding.UTF8))
|
{
|
_configBackupStr = reader.ReadToEnd();
|
try
|
{
|
config = JsonConvert.DeserializeObject<IProcessConfig>(_configBackupStr, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
|
}
|
catch (Exception ex)
|
{
|
LogAsync(DateTime.Now, $"配置反序列化异常", ex.GetExceptionMessage());
|
config = null;
|
}
|
}
|
}
|
|
if (config == null)
|
{
|
var attr = GetType().GetCustomAttribute<ProcessAttribute>();
|
|
if (attr != null)
|
{
|
config = ProcessFactory.CreateStationConfig(attr.ProcessCode, out string msg);
|
|
if (!string.IsNullOrWhiteSpace(msg))
|
{
|
throw new ProcessException($"流程配置创建失败。{msg}");
|
}
|
}
|
else
|
{
|
throw new ProcessException($"未能获取流程代码特性");
|
}
|
|
}
|
|
return config;
|
}
|
|
public void SaveProcessConfig(IProcessConfig config)
|
{
|
if (config == null)
|
throw new ProcessException("保存的配置信息不能为空");
|
|
string newConfig = JsonConvert.SerializeObject(config, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
|
using (StreamWriter writer = new StreamWriter(_configPath, false, System.Text.Encoding.UTF8))
|
{
|
writer.Write(newConfig);
|
writer.Flush();
|
writer.Close();
|
}
|
|
if (_configBackupStr != newConfig)
|
{
|
SaveBackupConfig();
|
}
|
}
|
|
private void SaveBackupConfig()
|
{
|
string backPath = Path.GetDirectoryName(_configPath);
|
backPath = Path.Combine(backPath, ProductionCode + "_bk");
|
if (!Directory.Exists(backPath))
|
{
|
Directory.CreateDirectory(backPath);
|
}
|
|
backPath = Path.Combine(backPath, $"Config_{ProductionCode}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.json");
|
|
using (StreamWriter writer = new StreamWriter(backPath, false, System.Text.Encoding.UTF8))
|
{
|
writer.Write(_configBackupStr);
|
writer.Flush();
|
writer.Close();
|
}
|
}
|
#endregion
|
|
#region MethodInfo解析
|
public List<ProcessMethodAttribute> ProcessMethodCollection { get; set; } = new List<ProcessMethodAttribute>();
|
|
/// <summary>
|
/// 调用方法的字典集合
|
/// Key:MethodCode,Value:MethodInfo
|
/// </summary>
|
public Dictionary<string, MethodInfo> ProcessMethodDict { get; set; } = new Dictionary<string, MethodInfo>();
|
|
/// <summary>
|
/// Halcon算法工具字典,在初始化时统一一次性载入
|
/// Key:MethodCode,Value:HalconTool
|
/// </summary>
|
protected Dictionary<string, HDevEngineTool> _halconToolDict = new Dictionary<string, HDevEngineTool>();
|
|
public virtual void InitialProcessMethods()
|
{
|
ProcessMethodDict = new Dictionary<string, MethodInfo>();
|
var methods = GetType().GetMethods().ToList();
|
methods.ForEach(m =>
|
{
|
var attr = m.GetCustomAttribute<ProcessMethodAttribute>();
|
if (attr != null)
|
{
|
ProcessMethodDict[attr.MethodCode] = m;
|
ProcessMethodCollection.Add(attr);
|
}
|
});
|
|
#region 初始化HalconTool 根据配置的接口类型来配置
|
InitialHalconTool();
|
#endregion
|
}
|
#endregion
|
|
#region Halcon算子设置
|
protected virtual void InitialHalconTool()
|
{
|
foreach (HDevEngineTool tool in _halconToolDict.Values)
|
{
|
tool?.Dispose();
|
}
|
|
_halconToolDict = new Dictionary<string, HDevEngineTool>();
|
|
IConfig.GetAllMonitorSet().ForEach(monitorSet =>
|
{
|
if (monitorSet.OpConfig is IHalconToolPath toolPath)
|
{
|
toolPath.GetHalconToolPathList().ForEach(path =>
|
{
|
LoadHalconTool(path, monitorSet.Id);
|
});
|
}
|
});
|
}
|
|
protected void LoadHalconTool(string algorithemPath, string prefix)
|
{
|
if (!string.IsNullOrWhiteSpace(algorithemPath))
|
{
|
string directoryPath = Path.GetDirectoryName(algorithemPath);
|
string fileName = Path.GetFileNameWithoutExtension(algorithemPath);
|
|
HDevEngineTool tool = new HDevEngineTool(directoryPath);
|
tool.LoadProcedure(fileName);
|
|
//使用“|”作为间隔符
|
_halconToolDict[prefix + "|" + algorithemPath] = tool;
|
}
|
}
|
|
/// <summary>
|
/// 获取预先载入的Halcon算法
|
/// </summary>
|
/// <param name="config">操作配置,用来确认监听来源和算法路径</param>
|
/// <param name="algorithemPath">算法路径,默认是配置中的第一个算法路径</param>
|
/// <returns>Halcon算法</returns>
|
protected HDevEngineTool GetHalconTool(IOperationConfig config, string algorithemPath = "")
|
{
|
if (string.IsNullOrWhiteSpace(algorithemPath))
|
{
|
algorithemPath = (config as IHalconToolPath)?.GetHalconToolPathList()[0];
|
}
|
|
string key = config.MonitorSetId + "|" + algorithemPath;
|
if (_halconToolDict.ContainsKey(key))
|
{
|
return _halconToolDict[key];
|
}
|
else
|
{
|
throw new ProcessException($"未能获取路径:{algorithemPath} 的算法");
|
}
|
}
|
|
/// <summary>
|
/// 重置或者重新载入配置中的Halcon算法
|
/// </summary>
|
/// <param name="config"></param>
|
/// <param name="algorithemPath"></param>
|
internal void ResetHalconTool(IOperationConfig config)
|
{
|
string algorithemPath = (config as IHalconToolPath)?.GetHalconToolPathList()[0];
|
string key = config.MonitorSetId + "|" + algorithemPath;
|
|
if (_halconToolDict.ContainsKey(key))
|
{
|
_halconToolDict[key].Dispose();
|
}
|
|
string directoryPath = Path.GetDirectoryName(algorithemPath);
|
string fileName = Path.GetFileNameWithoutExtension(algorithemPath);
|
|
HDevEngineTool tool = new HDevEngineTool(directoryPath);
|
tool.LoadProcedure(fileName);
|
|
_halconToolDict[key] = tool;
|
}
|
#endregion
|
|
#region IMonitor监听
|
protected virtual void OnMonitorInvoke(DateTime dt, string deviceId, IDevice sourceDevice, IMonitorSet monitorSet)
|
{
|
string methodCode = monitorSet.MethodCode;
|
|
if (!AuthorityCheck.IsAuthorityOK)
|
{
|
LogAsync(DateTime.Now, "权限检测失败", $"权限检测失败,方法{methodCode}无法执行,请联系相关人员");
|
monitorSet.Response = new ProcessResponse((int)ReturnValue.UNAUTHORIZATION);
|
return;
|
}
|
|
IOperationConfig config = monitorSet.OpConfig;
|
object res = null;
|
int reTryTimes = config.ReTryTimes;
|
IDevice device = DeviceCollection.FirstOrDefault(u => u.Id == deviceId);
|
LogAsync(DateTime.Now, $"{device.Name}调用{methodCode}开始", "");
|
|
Stopwatch sw = new Stopwatch();
|
sw.Start();
|
|
do
|
{
|
try
|
{
|
//有IOperationConfig参数的调用
|
res = ProcessMethodDict[methodCode].Invoke(this, new object[] { config, device, sourceDevice });
|
reTryTimes = -1;
|
}
|
catch (Exception invokeEx) //流程动作异常失败
|
{
|
Exception ex = invokeEx.InnerException ?? invokeEx;
|
reTryTimes--;
|
|
if (reTryTimes <= 0) //如果没有重试次数了就通知PLC
|
{
|
if (config == null || config.ExceptionValue == 0)
|
{
|
if (!(ex is ProcessException))
|
{
|
//如果是算法异常,返回NG值;否则返回异常值
|
if (ex is HDevEngineException)
|
{
|
res = new ProcessResponse((int)ReturnValue.NGVALUE);
|
}
|
else
|
{
|
res = new ProcessResponse((int)ReturnValue.EXCEPTIONVALUE);
|
}
|
|
var newEx = new ProcessException("函数" + methodCode + "执行异常", ex, ExceptionLevel.Warning);
|
}
|
else
|
{
|
if ((ex as ProcessException).InnerException != null)
|
{
|
res = new ProcessResponse((int)ReturnValue.EXCEPTIONVALUE);
|
}
|
else
|
{
|
res = new ProcessResponse((int)ReturnValue.NGVALUE);
|
}
|
}
|
}
|
else
|
{
|
res = new ProcessResponse(config.ExceptionValue);
|
}
|
|
LogAsync(DateTime.Now, methodCode + "异常信息", ex.GetExceptionMessage());
|
}
|
}
|
|
if (reTryTimes > 0)
|
{
|
LogAsync(DateTime.Now, methodCode + " reTryTimes", reTryTimes.ToString());
|
}
|
} while (reTryTimes > 0);
|
|
#region 设置返回值
|
monitorSet.Response = res as ProcessResponse;
|
|
//测试模式下始终反馈OK信号
|
if (IConfig.IsDemoMode && monitorSet.Response.ResultValue <= 0)
|
{
|
monitorSet.Response.ResultValue = (int)ReturnValue.OKVALUE;
|
}
|
#endregion
|
|
sw.Stop();
|
LogAsync(DateTime.Now, $"{device.Name}调用{methodCode}完成,耗时{sw.ElapsedMilliseconds}ms", "");
|
TimeRecordCSV(DateTime.Now, device.Name, $"{methodCode}调用完成", (int)sw.ElapsedMilliseconds);
|
}
|
#endregion
|
|
#region 图像处理
|
protected HImage CollectHImage(CameraBase camera, IOperationConfig opConfig, out string imgSetId, [CallerMemberName]string methodCode = "")
|
{
|
IImageSet set = null;
|
|
if (IConfig.IsImageOffline)
|
{
|
using (OfflineImageFrm oiF = new OfflineImageFrm())
|
{
|
if (oiF.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
{
|
set = new ImageSet
|
{
|
HImage = oiF.HImg,
|
ImageSaveOption = (opConfig as CameraOprerationConfigBase).ImageSaveOption
|
};
|
|
camera.NewImageSet(set);
|
camera.SaveOriginImage(oiF.Image, oiF.Image, set.Id);
|
}
|
else
|
{
|
throw new ProcessException("未能获取离线图片!");
|
}
|
}
|
}
|
else
|
{
|
Stopwatch sw = new Stopwatch();
|
sw.Start();
|
|
var cameraConifg = opConfig as CameraOprerationConfigBase;
|
if (cameraConifg.DelayBefore > 0)
|
{
|
Thread.Sleep(cameraConifg.DelayBefore);
|
}
|
|
camera.UploadOperationConfig(opConfig);
|
set = camera.Snapshot(opConfig);
|
|
if (cameraConifg.DelayAfter > 0)
|
{
|
Thread.Sleep(cameraConifg.DelayAfter);
|
}
|
|
sw.Stop();
|
LogAsync(DateTime.Now, $"{methodCode}采图耗时:{sw.ElapsedMilliseconds}ms", "");
|
TimeRecordCSV(DateTime.Now, camera.Name, methodCode + "采图", (int)sw.ElapsedMilliseconds);
|
}
|
|
imgSetId = set?.Id;
|
return set.HImage;
|
}
|
|
protected async void CameraUpdateImage(CameraBase camera, Bitmap image, string setId)
|
{
|
await Task.Run(() =>
|
{
|
OnBitmapOutput?.Invoke(camera.InitialConfig.Id, image, setId);
|
});
|
}
|
#endregion
|
|
#region 报警和DownTime
|
ObservableCollection<IWarningSet> _warningRemains = new ObservableCollection<IWarningSet>();
|
|
protected virtual void OnMonitorAlarm(DateTime dt, IDevice device, IWarningSet warning)
|
{
|
if (warning.CurrentStatus)
|
{
|
if (!_warningRemains.Any(u => u.WarningCode == warning.WarningCode))
|
{
|
_warningRemains.Add(warning);
|
}
|
}
|
else
|
{
|
var ws = _warningRemains.FirstOrDefault(u => u.WarningCode == warning.WarningCode);
|
if (ws != null)
|
{
|
_warningRemains.Remove(ws);
|
}
|
}
|
}
|
|
private void WarningRemains_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
{
|
string alarmMsg = "";
|
if (_warningRemains.Count > 0)
|
{
|
alarmMsg = string.Join("; ", _warningRemains.Select(u => $"{u.TriggerTime.ToString("HH:mm:ss")} {u.Source} 报警:{u.WarningDescription}"));
|
}
|
|
OnAlarmUpdate?.BeginInvoke(alarmMsg, null, null);
|
}
|
#endregion
|
|
#region ILogger
|
public LoggerHelper LoggerHelper { get; set; } = new LoggerHelper();
|
public virtual void LogAsync(DateTime dt, string prefix, string msg)
|
{
|
OnLog?.BeginInvoke(dt, prefix, msg, null, null);
|
if (IConfig.IsLogEnabled)
|
{
|
LoggerHelper.LogAsync(dt, prefix, msg);
|
}
|
}
|
|
protected void OnDeviceLog(DateTime dt, string prefix, string msg)
|
{
|
OnLog?.BeginInvoke(dt, prefix, msg, null, null);
|
}
|
|
static readonly object _lockTimeCSV = new object();
|
private async void TimeRecordCSV(DateTime dt, string deviceName, string prefix, int ms)
|
{
|
await Task.Run(() =>
|
{
|
lock (_lockTimeCSV)
|
{
|
string filePath = Path.Combine(IConfig.LogPath, $"TimeRecords_{DateTime.Now.ToString("yyyyMMdd")}.csv");
|
bool isFileExisted = File.Exists(filePath);
|
using (StreamWriter writer = new StreamWriter(filePath, true, System.Text.Encoding.UTF8))
|
{
|
if (!isFileExisted)
|
{
|
writer.WriteLine("Time,Device,Prefix,Consumed");
|
}
|
writer.WriteLine($"{dt.ToString("HH:mm:ss.fff")},{deviceName},{prefix},{ms}");
|
writer.Flush();
|
writer.Close();
|
}
|
}
|
});
|
}
|
#endregion
|
|
#region 临时数据保存和读取
|
protected static Dictionary<string, object> _tempDataLock = new Dictionary<string, object>();
|
protected string _tempFileDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TempData");
|
|
protected virtual T ReadTempDataFromHistory<T>(T t, string propertyName) where T : class
|
{
|
if (!_tempDataLock.ContainsKey(propertyName))
|
{
|
_tempDataLock[propertyName] = new object();
|
}
|
|
if (t != null)
|
{
|
return t;
|
}
|
else
|
{
|
if (!Directory.Exists(_tempFileDirectory))
|
{
|
Directory.CreateDirectory(_tempFileDirectory);
|
}
|
|
string filePath = Path.Combine(_tempFileDirectory, propertyName);
|
|
if (!File.Exists(filePath))
|
{
|
return null;
|
}
|
|
lock (_tempDataLock[propertyName])
|
{
|
using (StreamReader reader = new StreamReader(filePath, System.Text.Encoding.UTF8))
|
{
|
string content = reader.ReadToEnd();
|
var result = JsonConvert.DeserializeObject<T>(content);
|
return result;
|
}
|
}
|
}
|
}
|
|
protected virtual void SaveTempData<T>(T t, string propertyName, string defaultStr = "")
|
{
|
//await Task.Run(() =>
|
{
|
if (!_tempDataLock.ContainsKey(propertyName))
|
{
|
_tempDataLock[propertyName] = new object();
|
}
|
|
lock (_tempDataLock[propertyName])
|
{
|
try
|
{
|
if (!Directory.Exists(_tempFileDirectory))
|
{
|
Directory.CreateDirectory(_tempFileDirectory);
|
}
|
|
string serialStr = JsonConvert.SerializeObject(t);
|
|
if (!string.IsNullOrWhiteSpace(defaultStr))
|
{
|
if (t == null)
|
{
|
serialStr = defaultStr;
|
}
|
}
|
else
|
{
|
if (t == null)
|
return;
|
}
|
|
string filePath = Path.Combine(_tempFileDirectory, propertyName);
|
|
using (StreamWriter writer = new StreamWriter(filePath, false, System.Text.Encoding.UTF8))
|
{
|
writer.Write(serialStr);
|
writer.Flush();
|
writer.Close();
|
}
|
}
|
catch (Exception)
|
{
|
}
|
}
|
}
|
//);
|
}
|
#endregion
|
|
#region 结果上传
|
public Action<DateTime, int> OnUpdateResult { get; set; }
|
public Action<float> OnUpdateCT { get; set; }
|
#endregion
|
}
|
|
public enum ReturnValue
|
{
|
OKVALUE = 1,
|
NGVALUE = -1,
|
EXCEPTIONVALUE = -2,
|
UNAUTHORIZATION = -10,
|
}
|
}
|