timer.Elapsed += (o, e) =>

本文介绍了如何使用Lambda表达式来替代匿名委托,通过具体的代码示例展示了这种语法糖的用法,使得代码更加简洁易读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  //timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            //private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
            // {
            //    //....
            // }
            //(o,e)相当于上面的代码

            timer.Elapsed += (o, e) => {    }



Lambda 表达式 http://msdn.microsoft.com/zh-cn/library/bb397687.aspx
来代替匿名委托。


http://blog.youkuaiyun.com/chinacsharper/article/details/11880559

package com.tongchuang.realtime.mds; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tongchuang.realtime.bean.ULEParamConfig; import com.tongchuang.realtime.util.KafkaUtils; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.state.; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.kafka.source.KafkaSource; import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer; import org.apache.flink.streaming.api.datastream.; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction; import org.apache.flink.streaming.api.functions.source.RichSourceFunction; import org.apache.flink.util.Collector; import java.io.Serializable; import java.sql.; import java.text.SimpleDateFormat; import java.util.; import java.util.Date; import java.util.concurrent.TimeUnit; public class ULEDataanomalyanalysis { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // 创建Kafka消费者 KafkaSource<String> kafkaConsumer = KafkaUtils.getKafkaConsumer( "realdata_minute", "minutedata_uledataanomalyanalysis", OffsetsInitializer.latest() ); DataStreamSource<String> kafkaDS = env.fromSource( kafkaConsumer, WatermarkStrategy.noWatermarks(), "realdata_uledataanomalyanalysis" ); // 解析JSON并拆分每个tag的数据 SingleOutputStreamOperator<JSONObject> splitStream = kafkaDS .map(JSON::parseObject) .flatMap((JSONObject value, Collector<JSONObject> out) -> { JSONObject data = value.getJSONObject("datas"); String time = value.getString("times"); for (String tag : data.keySet()) { JSONObject tagData = data.getJSONObject(tag); JSONObject newObj = new JSONObject(); newObj.put("time", time); newObj.put("tag", tag); newObj.put("ontime", tagData.getDouble("ontime")); newObj.put("avg", tagData.getDouble("avg")); out.collect(newObj); } }) .returns(TypeInformation.of(JSONObject.class)) .name("Split-By-Tag"); // 每5分钟加载参数配置 DataStream<ConfigCollection> configDataStream = env .addSource(new MysqlConfigSource()) .setParallelism(1) .filter(Objects::nonNull) .name("Config-Source"); // 将配置流转换为广播流 BroadcastStream<ConfigCollection> configBroadcastStream = configDataStream .broadcast(Descriptors.configStateDescriptor); // 按tag分组并连接广播流 KeyedStream<JSONObject, String> keyedStream = splitStream .keyBy(json -> json.getString("tag")); BroadcastConnectedStream<JSONObject, ConfigCollection> connectedStream = keyedStream.connect(configBroadcastStream); // 异常检测处理 SingleOutputStreamOperator<JSONObject> anomalyStream = connectedStream .process(new OptimizedAnomalyDetectionFunction()) .name("Anomaly-Detection"); anomalyStream.print("异常检测结果"); // anomalyStream.map(JSON::toString).addSink(KafkaUtils.getKafkaSink("minutedata_uleanomaly")); env.execute("uledataanomalyanalysis"); } // 配置集合类 - 修复配置变化检测问题 public static class ConfigCollection implements Serializable { private static final long serialVersionUID = 1L; public final Map<String, List<ULEParamConfig>> tagToConfigs; public final Map<String, ULEParamConfig> encodeToConfig; private final long contentHash; // 添加内容哈希值用于精确检测变化 public ConfigCollection(Map<String, List<ULEParamConfig>> tagToConfigs, Map<String, ULEParamConfig> encodeToConfig) { this.tagToConfigs = new HashMap<>(tagToConfigs); this.encodeToConfig = new HashMap<>(encodeToConfig); // 计算内容哈希值(考虑所有配置项的内容) this.contentHash = calculateContentHash(); } private long calculateContentHash() { long hash = 0L; // 对每个配置项计算哈希(使用所有字段) for (ULEParamConfig config : encodeToConfig.values()) { hash = 31 * hash + Objects.hash( config.tag, config.encode, config.datatype, config.constantvalue, config.isonline, config.issync, config.syncparaencode, config.iszero, config.ishigh, config.highthreshold, config.islow, config.lowthreshold, config.duration ); } return hash; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ConfigCollection that = (ConfigCollection) o; return contentHash == that.contentHash; } @Override public int hashCode() { return (int) (contentHash ^ (contentHash >>> 32)); } public int configCount() { return encodeToConfig.size(); } } // MySQL配置源 - 优化配置加载和变化检测 public static class MysqlConfigSource extends RichSourceFunction<ConfigCollection> { private volatile boolean isRunning = true; private final long interval = TimeUnit.MINUTES.toMillis(5); private transient ConfigCollection lastConfig; private long lastConfigHash = 0; @Override public void run(SourceContext<ConfigCollection> ctx) throws Exception { while (isRunning) { ConfigCollection newConfig = loadParams(); if (newConfig != null) { long newHash = newConfig.contentHash; if (lastConfig == null || newHash != lastConfigHash) { ctx.collect(newConfig); lastConfig = newConfig; lastConfigHash = newHash; System.out.println("检测到配置变化,广播新配置 (哈希值: " + newHash + ")"); } else { System.out.println("配置未变化,跳过广播 (哈希值: " + newHash + ")"); } } else { System.out.println("配置加载失败,使用上次配置"); } Thread.sleep(interval); } } private ConfigCollection loadParams() { Map<String, List<ULEParamConfig>> tagToConfigs = new HashMap<>(5000); Map<String, ULEParamConfig> encodeToConfig = new HashMap<>(5000); String url = "jdbc:mysql://10.51.37.73:3306/eps?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; String user = "root"; String password = "6CKIm5jDVsLrahSw"; String query = "SELECT F_tag AS tag, F_enCode AS encode, F_dataTypes AS datatype, " + "F_isConstantValue AS constantvalue, F_isOnline AS isonline, " + "F_isSync AS issync, F_syncParaEnCode AS syncparaencode, " + "F_isZero AS iszero, F_isHigh AS ishigh, F_highThreshold AS highthreshold, " + "F_isLow AS islow, F_lowThreshold AS lowthreshold, F_duration AS duration " + "FROM t_equipmentparameter " + "WHERE F_enabledmark = '1' AND (F_isConstantValue ='1' OR F_isZero= '1' " + "OR F_isHigh = '1' OR F_isLow = '1' OR F_isOnline = '1' OR F_isSync = '1')"; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { while (rs.next()) { ULEParamConfig config = new ULEParamConfig(); config.tag = rs.getString("tag"); config.encode = rs.getString("encode"); config.datatype = rs.getString("datatype"); config.constantvalue = rs.getInt("constantvalue"); config.iszero = rs.getInt("iszero"); config.ishigh = rs.getInt("ishigh"); config.highthreshold = rs.getDouble("highthreshold"); config.islow = rs.getInt("islow"); config.lowthreshold = rs.getDouble("lowthreshold"); config.duration = rs.getLong("duration"); config.isonline = rs.getInt("isonline"); config.issync = rs.getInt("issync"); config.syncparaencode = rs.getString("syncparaencode"); String tag = config.tag; tagToConfigs.computeIfAbsent(tag, k -> new ArrayList<>(10)).add(config); encodeToConfig.put(config.encode, config); } System.out.println("加载配置: " + encodeToConfig.size() + " 个参数"); return new ConfigCollection(tagToConfigs, encodeToConfig); } catch (SQLException e) { System.err.println("加载参数配置错误:"); e.printStackTrace(); return null; } } @Override public void cancel() { isRunning = false; } } // 状态描述符 public static class Descriptors { public static final MapStateDescriptor<Void, ConfigCollection> configStateDescriptor = new MapStateDescriptor<>( "configState", TypeInformation.of(Void.class), TypeInformation.of(ConfigCollection.class) ); } // 优化后的异常检测函数(带状态TTL和离线检测优化) public static class OptimizedAnomalyDetectionFunction extends KeyedBroadcastProcessFunction<String, JSONObject, ConfigCollection, JSONObject> { // 状态管理 private transient MapState<String, AnomalyState> stateMap; // key=encode private transient MapState<String, Double> lastValuesMap; // key=tag private transient MapState<String, Long> lastDataTimeMap; // key=tag private transient MapState<String, Long> offlineTimerState; // key=encode private transient SimpleDateFormat timeFormat; @Override public void open(Configuration parameters) { // 状态TTL配置(30天自动清理) StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(3000)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() .build(); // 初始化异常状态存储(启用TTL) MapStateDescriptor<String, AnomalyState> stateDesc = new MapStateDescriptor<>( "anomalyState", BasicTypeInfo.STRING_TYPE_INFO, TypeInformation.of(AnomalyState.class) ); stateDesc.enableTimeToLive(ttlConfig); stateMap = getRuntimeContext().getMapState(stateDesc); // 初始化最新值存储(启用TTL) MapStateDescriptor<String, Double> valuesDesc = new MapStateDescriptor<>( "lastValuesState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); valuesDesc.enableTimeToLive(ttlConfig); lastValuesMap = getRuntimeContext().getMapState(valuesDesc); // 初始化最后数据时间存储(启用TTL) MapStateDescriptor<String, Long> timeDesc = new MapStateDescriptor<>( "lastDataTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timeDesc.enableTimeToLive(ttlConfig); lastDataTimeMap = getRuntimeContext().getMapState(timeDesc); // 初始化离线定时器状态(启用TTL) MapStateDescriptor<String, Long> timerDesc = new MapStateDescriptor<>( "offlineTimerState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timerDesc.enableTimeToLive(ttlConfig); offlineTimerState = getRuntimeContext().getMapState(timerDesc); timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); } @Override public void processElement(JSONObject data, ReadOnlyContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); String timeStr = data.getString("time"); long eventTime = timeFormat.parse(timeStr).getTime(); // 更新最后数据时间 lastDataTimeMap.put(tag, eventTime); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) { return; } List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null || configs.isEmpty()) { return; } // ========== 清理无效状态 ========== Iterator<String> stateKeys = stateMap.keys().iterator(); while (stateKeys.hasNext()) { String encode = stateKeys.next(); boolean found = false; for (ULEParamConfig cfg : configs) { if (cfg.encode.equals(encode)) { found = true; break; } } if (!found) { stateMap.remove(encode); // 清理相关定时器 Long timer = offlineTimerState.get(encode); if (timer != null) { ctx.timerService().deleteEventTimeTimer(timer); offlineTimerState.remove(encode); } } } double value = 0; boolean valueSet = false; // 遍历配置项进行异常检测 for (ULEParamConfig config : configs) { if (!valueSet) { value = "436887485805570949".equals(config.datatype) ? data.getDouble("ontime") : data.getDouble("avg"); lastValuesMap.put(tag, value); valueSet = true; } // 获取或初始化状态 AnomalyState state = getOrCreateState(config.encode); // ========== 离线恢复检测 ========== if (config.isonline == 1) { AnomalyStatus status = state.getStatus(5); if (status.reported) { // 设备重新上线,发送恢复事件 reportAnomaly(5, 0, 0.0, timeStr, config, out); status.reset(); System.out.println("设备 " + tag + " 恢复在线,发送恢复事件"); } } // 处理异常类型 checkConstantValueAnomaly(config, value, timeStr, state, out); checkZeroValueAnomaly(config, value, timeStr, state, out); checkThresholdAnomaly(config, value, timeStr, state, out); checkSyncAnomaly(config, value, timeStr, state, configCollection, out); // 设置离线检测定时器(仅当配置开启离线检测时) if (config.isonline == 1) { setupOfflineTimer(tag, config, eventTime, ctx); } // 保存状态 stateMap.put(config.encode, state); } } // 设置离线检测定时器 private void setupOfflineTimer(String tag, ULEParamConfig config, long eventTime, ReadOnlyContext ctx) throws Exception { long durationMillis = config.duration * 60 * 1000; long timerTimestamp = eventTime + durationMillis; String timerKey = config.encode; // 使用encode作为唯一标识 // 清除旧定时器(如果存在) Long oldTimer = offlineTimerState.get(timerKey); if (oldTimer != null) { ctx.timerService().deleteEventTimeTimer(oldTimer); } // 注册新定时器(事件时间) ctx.timerService().registerEventTimeTimer(timerTimestamp); offlineTimerState.put(timerKey, timerTimestamp); } @Override public void onTimer(long timestamp, OnTimerContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) return; List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null) return; // 找出匹配的定时器 List<ULEParamConfig> triggeredConfigs = new ArrayList<>(); for (ULEParamConfig config : configs) { if (config.isonline == 1) { Long storedTimer = offlineTimerState.get(config.encode); if (storedTimer != null && storedTimer == timestamp) { triggeredConfigs.add(config); } } } // 处理所有触发的配置项 for (ULEParamConfig config : triggeredConfigs) { Long lastEventTime = lastDataTimeMap.get(tag); AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); // 5=离线类型 if (lastEventTime == null || lastEventTime < timestamp - config.duration * 60 * 1000) { // 触发离线异常 if (!status.reported) { reportAnomaly(5, 1, 0.0, timeFormat.format(new Date(timestamp)), config, out); status.reported = true; System.out.println("设备 " + tag + " 离线报告"); } } else { // 取消误报 if (status.reported) { reportAnomaly(5, 0, 0.0, timeFormat.format(new Date()), config, out); status.reported = false; System.out.println("设备 " + tag + " 离线恢复"); } } // 重置定时器 offlineTimerState.remove(config.encode); } } // 恒值检测 private void checkConstantValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.constantvalue != 1) return; try { AnomalyStatus status = state.getStatus(1); long durationThreshold = config.duration * 60 * 1000; Date timestamp = timeFormat.parse(timeStr); if (status.lastValue == null) { status.lastValue = currentValue; status.lastChangeTime = timestamp; return; } if (Math.abs(currentValue - status.lastValue) > 0.001) { status.lastValue = currentValue; status.lastChangeTime = timestamp; if (status.reported) { reportAnomaly(1, 0, currentValue, timeStr, config, out); } status.reset(); return; } long elapsed = timestamp.getTime() - status.lastChangeTime.getTime(); if (elapsed > durationThreshold) { if (!status.reported) { reportAnomaly(1, 1, currentValue, timeStr, config, out); status.reported = true; } } } catch (Exception e) { System.err.println("恒值检测错误: " + config.encode + " - " + e.getMessage()); } } // 零值检测 private void checkZeroValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.iszero != 1) return; try { AnomalyStatus status = state.getStatus(2); Date timestamp = timeFormat.parse(timeStr); boolean isZero = Math.abs(currentValue) < 0.001; if (isZero) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(2, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(2, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("零值检测错误: " + config.encode + " - " + e.getMessage()); } } // 阈值检测 private void checkThresholdAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { try { if (config.ishigh == 1) { AnomalyStatus highStatus = state.getStatus(3); processThresholdAnomaly(highStatus, currentValue, timeStr, currentValue > config.highthreshold, config, 3, out); } if (config.islow == 1) { AnomalyStatus lowStatus = state.getStatus(4); processThresholdAnomaly(lowStatus, currentValue, timeStr, currentValue < config.lowthreshold, config, 4, out); } } catch (Exception e) { System.err.println("阈值检测错误: " + config.encode + " - " + e.getMessage()); } } private void processThresholdAnomaly(AnomalyStatus status, double currentValue, String timeStr, boolean isAnomaly, ULEParamConfig config, int anomalyType, Collector<JSONObject> out) { try { Date timestamp = timeFormat.parse(timeStr); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(anomalyType, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(anomalyType, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("阈值处理错误: " + config.encode + " - " + e.getMessage()); } } // 同步检测 private void checkSyncAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, ConfigCollection configCollection, Collector<JSONObject> out) { if (config.issync != 1 || config.syncparaencode == null) return; try { AnomalyStatus status = state.getStatus(6); Date timestamp = timeFormat.parse(timeStr); ULEParamConfig relatedConfig = configCollection.encodeToConfig.get(config.syncparaencode); if (relatedConfig == null) return; String relatedTag = null; for (Map.Entry<String, List<ULEParamConfig>> entry : configCollection.tagToConfigs.entrySet()) { if (entry.getValue().contains(relatedConfig)) { relatedTag = entry.getKey(); break; } } if (relatedTag == null) return; Double relatedValue = lastValuesMap.get(relatedTag); if (relatedValue == null) return; boolean isAnomaly = (Math.abs(currentValue - 1.0) < 0.001) && (Math.abs(relatedValue) < 0.001); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(6, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(6, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("同步检测错误: " + config.encode + " - " + e.getMessage()); } } // 报告异常 private void reportAnomaly(int anomalyType, int statusFlag, double value, String time, ULEParamConfig config, Collector<JSONObject> out) { JSONObject event = new JSONObject(); event.put("tag", config.tag); event.put("paracode", config.encode); event.put("abnormaltype", anomalyType); event.put("statusflag", statusFlag); event.put("datavalue", value); event.put("triggertime", time); out.collect(event); } @Override public void processBroadcastElement(ConfigCollection newConfig, Context ctx, Collector<JSONObject> out) { BroadcastState<Void, ConfigCollection> state = ctx.getBroadcastState(Descriptors.configStateDescriptor); try { state.put(null, newConfig); // 仅更新广播状态 } catch (Exception e) { e.printStackTrace(); } System.out.println("广播配置更新完成, 配置项: " + newConfig.configCount()); } // ========== 辅助方法 ========== private ConfigCollection getBroadcastConfig(ReadOnlyContext ctx) throws Exception { return ctx.getBroadcastState(Descriptors.configStateDescriptor).get(null); } private AnomalyState getOrCreateState(String encode) throws Exception { AnomalyState state = stateMap.get(encode); if (state == null) { state = new AnomalyState(); } return state; } } // 异常状态类 public static class AnomalyState implements Serializable { private static final long serialVersionUID = 1L; private final Map<Integer, AnomalyStatus> statusMap = new HashMap<>(); public AnomalyStatus getStatus(int type) { return statusMap.computeIfAbsent(type, k -> new AnomalyStatus()); } } // 异常状态详情 public static class AnomalyStatus implements Serializable { private static final long serialVersionUID = 1L; public Date startTime; // 异常开始时间 public Double lastValue; // 用于恒值检测 public Date lastChangeTime; // 值最后变化时间 public boolean reported; // 是否已报告 public void reset() { startTime = null; lastValue = null; lastChangeTime = null; reported = false; } } }上述代码需完善内容,1、离线监测未实现,当配置中有条的tag更改,且流数据中没有该tag,未报出离线异常;2、当参数调整时,已有的报警状态参数,配置成不启用相应的报警如何处理;3、当参数配置删除时,已有的报警状态参数处理。,请完善上述内容,生成完整优化后可运行代码。不考虑通过 ‘Context’ 中的方法 ‘timerService’和‘ReadOnlyContext’ 中的方法 ‘getTimerService’方法实现
08-02
using Com.Huancare.Medical.Lis.Communication.ProtocolBase.Tcp; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Timers; namespace Com.Huancare.Medical.Lis.Communication.Protocol.Tcp { /// <summary> /// 花丛卫生院 生化仪 zybio EXC800 /// </summary> internal class SvrEXC420 : TcpSvrReceiveData { private string iPort; /// <summary> /// 开始字节 /// </summary> private byte byteStart = 0x0B; /// <summary> /// 换行(回车)字节 /// </summary> private byte byteEnter = 0x0D; /// <summary> /// 结束字节 /// </summary> private byte byteEnd = 0x1C; private string strHosID; private string strHosEquipID; private string strPort; private System.Timers.Timer timer = new System.Timers.Timer(3000.0); private StringBuilder sbCache = new StringBuilder(); public SvrEXC420(string strHostIp, int iPort, int numConnections, int bufferSize) : base(strHostIp, iPort, numConnections, bufferSize) { this.strPort = iPort.ToString(); this.timer.Elapsed += new ElapsedEventHandler(this.SaveData); this.timer.AutoReset = false; } protected override void PacketReceived(byte[] btRecData, string strHosEquipID, string strHosID) { try { string strRecData = System.Text.Encoding.Default.GetString(btRecData); string[] strSplit = new string[] { "\r" }; string strNo = strRecData.Split(strSplit, StringSplitOptions.None)[0].Split('|')[9]; base.SetReturnData(ReceiveSucc(strNo)); } catch { } this.timer.Enabled = false; this.strHosID = strHosID; this.strHosEquipID = strHosEquipID; //this.sbCache.Append(CommonConfig.ENCODING_GB2312.GetString(btRecData)); this.sbCache.Append(Encoding.UTF8.GetString(btRecData)); this.timer.Enabled = true; } private void SaveData(object sender, ElapsedEventArgs e) { string state = ""; Monitor.Enter(this); try { state = this.sbCache.ToString(); this.sbCache.Remove(0, this.sbCache.Length); } finally { Monitor.Exit(this); } ThreadPool.QueueUserWorkItem(new WaitCallback(this.ThreadProcessReceiveData), state); } private void ThreadProcessReceiveData(object data) { string text = data.ToString(); string text2 = ""; string strOptMsg = ""; StringBuilder stringBuilder = new StringBuilder(); try { string[] sampleSplit = text.Split(new string[] { " MSH" }, StringSplitOptions.RemoveEmptyEntries); foreach (var item in sampleSplit) { string[] array = item.Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text3 = array2[i]; if (text3.IndexOf("OBR") > -1) { string text4 = text3.Substring(text3.IndexOf("OBR")); string[] array3 = text4.Split(new string[] { "|" }, StringSplitOptions.None); string value1 = DateTime.Now.ToString("yyyyMMddHHmmss"); if (array3.Length > 3) // 确保至少有4个元素(索引0,1,2,3) { string value2 = DateTime.Now.ToString("yyyyMMddHHmmss"); // 提取样本编号并清洗非数字字符 string sampleId = Regex.Replace(array3[3], @"[^\d]", ""); // 注意:这里将*改为无,因为*表示0个或多个,而我们要删除所有非数字,用空字符串替代 // 截取后4位数字(不足4位时取全部) string lastFourDigits = sampleId.Length >= 4 ? sampleId.Substring(sampleId.Length - 4) : sampleId; } else { // 处理数组长度不足的情况,例如记录错误或使用默认值 // 这里根据实际需求处理,例如追加一个默认值或跳过 // 示例:使用默认样本编号"0000" string value = DateTime.Now.ToString("yyyyMMddHHmmss"); stringBuilder.Append("0000").Append("|").Append(value).Append("|"); } text2 = array3[3];//样本编号 text2 = System.Text.RegularExpressions.Regex.Replace(text2, @"[^\d]*", ""); stringBuilder.Append(text2).Append("|").Append(value).Append("|"); } if (text3.IndexOf("|NM|") > -1) { string[] array4 = text3.Split(new string[] { "|","^" }, StringSplitOptions.None); string text5 = array4[4];//项目通道名称 string value2 = array4[6];//项目值 stringBuilder.Append(text5).Append(":").Append(value2).Append(";"); } } stringBuilder.Append("|"); strOptMsg = SaveReceiveData.SaveReceivePacked(this.strHosID, this.strHosEquipID, stringBuilder.ToString()); base.OnReceiveDatas(this.strHosEquipID, this.strPort, new ReceiveDatasEventArgs(Encoding.UTF8.GetBytes(stringBuilder.ToString()), strOptMsg)); stringBuilder.Remove(0, stringBuilder.Length); } } catch (Exception ex) { base.OnReceiveDatas(this.strHosEquipID, this.strPort, new ReceiveDatasEventArgs(Encoding.UTF8.GetBytes(stringBuilder.ToString()), strOptMsg)); strOptMsg = ex.Message; } } private readonly string MSH_Format = @"MSH|^~\&|||||{0}||ACK^R01|{1}|P|2.3.1||||0||UTF8|||"; private readonly string MSA_Format = @"MSA|AA|{0}|Message accepted|||0|"; private byte[] ReceiveSucc(string msgContorlID) { string strMSH = string.Format(MSH_Format, DateTime.Now.ToString("yyyyMMddHHmmss"), msgContorlID); byte[] byteMSH = System.Text.Encoding.ASCII.GetBytes(strMSH); byte[] byteTmp = new byte[byteMSH.Length + 2]; byteTmp[0] = byteStart; Array.Copy(byteMSH, 0, byteTmp, 1, byteMSH.Length); byteTmp[byteTmp.Length - 1] = byteEnter; byteMSH = byteTmp; string strMSA = string.Format(MSA_Format, msgContorlID); byte[] byteMSA = System.Text.Encoding.ASCII.GetBytes(strMSA); byteTmp = new byte[byteMSH.Length + byteMSA.Length + 3]; Array.Copy(byteMSH, 0, byteTmp, 0, byteMSH.Length); Array.Copy(byteMSA, 0, byteTmp, byteMSH.Length, byteMSA.Length); byteTmp[byteTmp.Length - 3] = byteEnter; byteTmp[byteTmp.Length - 2] = byteEnd; byteTmp[byteTmp.Length - 1] = byteEnter; return byteTmp; } } }
最新发布
08-02
package com.tongchuang.realtime.mds; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tongchuang.realtime.bean.ULEParamConfig; import com.tongchuang.realtime.util.KafkaUtils; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.state.*; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.kafka.source.KafkaSource; import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer; import org.apache.flink.streaming.api.datastream.*; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction; import org.apache.flink.streaming.api.functions.source.RichSourceFunction; import org.apache.flink.util.Collector; import java.io.Serializable; import java.sql.*; import java.text.SimpleDateFormat; import java.util.*; import java.util.Date; import java.util.concurrent.TimeUnit; public class ULEDataanomalyanalysis { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // 创建Kafka消费者 KafkaSource<String> kafkaConsumer = KafkaUtils.getKafkaConsumer( "realdata_minute", "minutedata_uledataanomalyanalysis", OffsetsInitializer.latest() ); DataStreamSource<String> kafkaDS = env.fromSource( kafkaConsumer, WatermarkStrategy.noWatermarks(), "realdata_uledataanomalyanalysis" ); // 解析JSON并拆分每个tag的数据 SingleOutputStreamOperator<JSONObject> splitStream = kafkaDS .map(JSON::parseObject) .flatMap((JSONObject value, Collector<JSONObject> out) -> { JSONObject data = value.getJSONObject("datas"); String time = value.getString("times"); for (String tag : data.keySet()) { JSONObject tagData = data.getJSONObject(tag); JSONObject newObj = new JSONObject(); newObj.put("time", time); newObj.put("tag", tag); newObj.put("ontime", tagData.getDouble("ontime")); newObj.put("avg", tagData.getDouble("avg")); out.collect(newObj); } }) .returns(TypeInformation.of(JSONObject.class)) .name("Split-By-Tag"); // 每5分钟加载参数配置 DataStream<ConfigCollection> configDataStream = env .addSource(new MysqlConfigSource()) .setParallelism(1) .filter(Objects::nonNull) .name("Config-Source"); // 将配置流转换为广播流 BroadcastStream<ConfigCollection> configBroadcastStream = configDataStream .broadcast(Descriptors.configStateDescriptor); // 按tag分组并连接广播流 KeyedStream<JSONObject, String> keyedStream = splitStream .keyBy(json -> json.getString("tag")); BroadcastConnectedStream<JSONObject, ConfigCollection> connectedStream = keyedStream.connect(configBroadcastStream); // 异常检测处理 SingleOutputStreamOperator<JSONObject> anomalyStream = connectedStream .process(new OptimizedAnomalyDetectionFunction()) .name("Anomaly-Detection"); anomalyStream.print("异常检测结果"); // anomalyStream.map(JSON::toString).addSink(KafkaUtils.getKafkaSink("minutedata_uleanomaly")); env.execute("uledataanomalyanalysis"); } // 配置集合类 - 修复配置变化检测问题 public static class ConfigCollection implements Serializable { private static final long serialVersionUID = 1L; public final Map<String, List<ULEParamConfig>> tagToConfigs; public final Map<String, ULEParamConfig> encodeToConfig; private final long contentHash; // 添加内容哈希值用于精确检测变化 public ConfigCollection(Map<String, List<ULEParamConfig>> tagToConfigs, Map<String, ULEParamConfig> encodeToConfig) { this.tagToConfigs = new HashMap<>(tagToConfigs); this.encodeToConfig = new HashMap<>(encodeToConfig); // 计算内容哈希值(考虑所有配置项的内容) this.contentHash = calculateContentHash(); } private long calculateContentHash() { long hash = 0L; // 对每个配置项计算哈希(使用所有字段) for (ULEParamConfig config : encodeToConfig.values()) { hash = 31 * hash + Objects.hash( config.tag, config.encode, config.datatype, config.constantvalue, config.isonline, config.issync, config.syncparaencode, config.iszero, config.ishigh, config.highthreshold, config.islow, config.lowthreshold, config.duration ); } return hash; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ConfigCollection that = (ConfigCollection) o; return contentHash == that.contentHash; } @Override public int hashCode() { return (int) (contentHash ^ (contentHash >>> 32)); } public int configCount() { return encodeToConfig.size(); } } // MySQL配置源 - 优化配置加载和变化检测 public static class MysqlConfigSource extends RichSourceFunction<ConfigCollection> { private volatile boolean isRunning = true; private final long interval = TimeUnit.MINUTES.toMillis(5); private transient ConfigCollection lastConfig; private long lastConfigHash = 0; @Override public void run(SourceContext<ConfigCollection> ctx) throws Exception { while (isRunning) { ConfigCollection newConfig = loadParams(); if (newConfig != null) { long newHash = newConfig.contentHash; if (lastConfig == null || newHash != lastConfigHash) { ctx.collect(newConfig); lastConfig = newConfig; lastConfigHash = newHash; System.out.println("检测到配置变化,广播新配置 (哈希值: " + newHash + ")"); } else { System.out.println("配置未变化,跳过广播 (哈希值: " + newHash + ")"); } } else { System.out.println("配置加载失败,使用上次配置"); } Thread.sleep(interval); } } private ConfigCollection loadParams() { Map<String, List<ULEParamConfig>> tagToConfigs = new HashMap<>(5000); Map<String, ULEParamConfig> encodeToConfig = new HashMap<>(5000); String url = "jdbc:mysql://10.51.37.73:3306/eps?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; String user = "root"; String password = "6CKIm5jDVsLrahSw"; String query = "SELECT F_tag AS tag, F_enCode AS encode, F_dataTypes AS datatype, " + "F_isConstantValue AS constantvalue, F_isOnline AS isonline, " + "F_isSync AS issync, F_syncParaEnCode AS syncparaencode, " + "F_isZero AS iszero, F_isHigh AS ishigh, F_highThreshold AS highthreshold, " + "F_isLow AS islow, F_lowThreshold AS lowthreshold, F_duration AS duration " + "FROM t_equipmentparameter " + "WHERE F_enabledmark = '1' AND (F_isConstantValue ='1' OR F_isZero= '1' " + "OR F_isHigh = '1' OR F_isLow = '1' OR F_isOnline = '1' OR F_isSync = '1')"; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { while (rs.next()) { ULEParamConfig config = new ULEParamConfig(); config.tag = rs.getString("tag"); config.encode = rs.getString("encode"); config.datatype = rs.getString("datatype"); config.constantvalue = rs.getInt("constantvalue"); config.iszero = rs.getInt("iszero"); config.ishigh = rs.getInt("ishigh"); config.highthreshold = rs.getDouble("highthreshold"); config.islow = rs.getInt("islow"); config.lowthreshold = rs.getDouble("lowthreshold"); config.duration = rs.getLong("duration"); config.isonline = rs.getInt("isonline"); config.issync = rs.getInt("issync"); config.syncparaencode = rs.getString("syncparaencode"); String tag = config.tag; tagToConfigs.computeIfAbsent(tag, k -> new ArrayList<>(10)).add(config); encodeToConfig.put(config.encode, config); } System.out.println("加载配置: " + encodeToConfig.size() + " 个参数"); return new ConfigCollection(tagToConfigs, encodeToConfig); } catch (SQLException e) { System.err.println("加载参数配置错误:"); e.printStackTrace(); return null; } } @Override public void cancel() { isRunning = false; } } // 状态描述符 public static class Descriptors { public static final MapStateDescriptor<Void, ConfigCollection> configStateDescriptor = new MapStateDescriptor<>( "configState", TypeInformation.of(Void.class), TypeInformation.of(ConfigCollection.class) ); } // 优化后的异常检测函数(带状态TTL和离线检测优化) public static class OptimizedAnomalyDetectionFunction extends KeyedBroadcastProcessFunction<String, JSONObject, ConfigCollection, JSONObject> { // 状态管理 private transient MapState<String, AnomalyState> stateMap; // key=encode private transient MapState<String, Double> lastValuesMap; // key=tag private transient MapState<String, Long> lastDataTimeMap; // key=tag private transient MapState<String, Long> offlineTimerState; // key=encode private transient SimpleDateFormat timeFormat; @Override public void open(Configuration parameters) { // 状态TTL配置(30天自动清理) StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(3000)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() .build(); // 初始化异常状态存储(启用TTL) MapStateDescriptor<String, AnomalyState> stateDesc = new MapStateDescriptor<>( "anomalyState", BasicTypeInfo.STRING_TYPE_INFO, TypeInformation.of(AnomalyState.class) ); stateDesc.enableTimeToLive(ttlConfig); stateMap = getRuntimeContext().getMapState(stateDesc); // 初始化最新值存储(启用TTL) MapStateDescriptor<String, Double> valuesDesc = new MapStateDescriptor<>( "lastValuesState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); valuesDesc.enableTimeToLive(ttlConfig); lastValuesMap = getRuntimeContext().getMapState(valuesDesc); // 初始化最后数据时间存储(启用TTL) MapStateDescriptor<String, Long> timeDesc = new MapStateDescriptor<>( "lastDataTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timeDesc.enableTimeToLive(ttlConfig); lastDataTimeMap = getRuntimeContext().getMapState(timeDesc); // 初始化离线定时器状态(启用TTL) MapStateDescriptor<String, Long> timerDesc = new MapStateDescriptor<>( "offlineTimerState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timerDesc.enableTimeToLive(ttlConfig); offlineTimerState = getRuntimeContext().getMapState(timerDesc); timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); } @Override public void processElement(JSONObject data, ReadOnlyContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); String timeStr = data.getString("time"); long eventTime = timeFormat.parse(timeStr).getTime(); // 更新最后数据时间 lastDataTimeMap.put(tag, eventTime); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) { return; } List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null || configs.isEmpty()) { return; } // ========== 清理无效状态 ========== Iterator<String> stateKeys = stateMap.keys().iterator(); while (stateKeys.hasNext()) { String encode = stateKeys.next(); boolean found = false; for (ULEParamConfig cfg : configs) { if (cfg.encode.equals(encode)) { found = true; break; } } if (!found) { stateMap.remove(encode); // 清理相关定时器 Long timer = offlineTimerState.get(encode); if (timer != null) { ctx.timerService().deleteEventTimeTimer(timer); offlineTimerState.remove(encode); } } } double value = 0; boolean valueSet = false; // 遍历配置项进行异常检测 for (ULEParamConfig config : configs) { if (!valueSet) { value = "436887485805570949".equals(config.datatype) ? data.getDouble("ontime") : data.getDouble("avg"); lastValuesMap.put(tag, value); valueSet = true; } // 获取或初始化状态 AnomalyState state = getOrCreateState(config.encode); // ========== 离线恢复检测 ========== if (config.isonline == 1) { AnomalyStatus status = state.getStatus(5); if (status.reported) { // 设备重新上线,发送恢复事件 reportAnomaly(5, 0, 0.0, timeStr, config, out); status.reset(); System.out.println("设备 " + tag + " 恢复在线,发送恢复事件"); } } // 处理异常类型 checkConstantValueAnomaly(config, value, timeStr, state, out); checkZeroValueAnomaly(config, value, timeStr, state, out); checkThresholdAnomaly(config, value, timeStr, state, out); checkSyncAnomaly(config, value, timeStr, state, configCollection, out); // 设置离线检测定时器(仅当配置开启离线检测时) if (config.isonline == 1) { setupOfflineTimer(tag, config, eventTime, ctx); } // 保存状态 stateMap.put(config.encode, state); } } // 设置离线检测定时器 private void setupOfflineTimer(String tag, ULEParamConfig config, long eventTime, ReadOnlyContext ctx) throws Exception { long durationMillis = config.duration * 60 * 1000; long timerTimestamp = eventTime + durationMillis; String timerKey = config.encode; // 使用encode作为唯一标识 // 清除旧定时器(如果存在) Long oldTimer = offlineTimerState.get(timerKey); if (oldTimer != null) { ctx.timerService().deleteEventTimeTimer(oldTimer); } // 注册新定时器(事件时间) ctx.timerService().registerEventTimeTimer(timerTimestamp); offlineTimerState.put(timerKey, timerTimestamp); } @Override public void onTimer(long timestamp, OnTimerContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) return; List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null) return; // 找出匹配的定时器 List<ULEParamConfig> triggeredConfigs = new ArrayList<>(); for (ULEParamConfig config : configs) { if (config.isonline == 1) { Long storedTimer = offlineTimerState.get(config.encode); if (storedTimer != null && storedTimer == timestamp) { triggeredConfigs.add(config); } } } // 处理所有触发的配置项 for (ULEParamConfig config : triggeredConfigs) { Long lastEventTime = lastDataTimeMap.get(tag); AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); // 5=离线类型 if (lastEventTime == null || lastEventTime < timestamp - config.duration * 60 * 1000) { // 触发离线异常 if (!status.reported) { reportAnomaly(5, 1, 0.0, timeFormat.format(new Date(timestamp)), config, out); status.reported = true; System.out.println("设备 " + tag + " 离线报告"); } } else { // 取消误报 if (status.reported) { reportAnomaly(5, 0, 0.0, timeFormat.format(new Date()), config, out); status.reported = false; System.out.println("设备 " + tag + " 离线恢复"); } } // 重置定时器 offlineTimerState.remove(config.encode); } } // 恒值检测 private void checkConstantValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.constantvalue != 1) return; try { AnomalyStatus status = state.getStatus(1); long durationThreshold = config.duration * 60 * 1000; Date timestamp = timeFormat.parse(timeStr); if (status.lastValue == null) { status.lastValue = currentValue; status.lastChangeTime = timestamp; return; } if (Math.abs(currentValue - status.lastValue) > 0.001) { status.lastValue = currentValue; status.lastChangeTime = timestamp; if (status.reported) { reportAnomaly(1, 0, currentValue, timeStr, config, out); } status.reset(); return; } long elapsed = timestamp.getTime() - status.lastChangeTime.getTime(); if (elapsed > durationThreshold) { if (!status.reported) { reportAnomaly(1, 1, currentValue, timeStr, config, out); status.reported = true; } } } catch (Exception e) { System.err.println("恒值检测错误: " + config.encode + " - " + e.getMessage()); } } // 零值检测 private void checkZeroValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.iszero != 1) return; try { AnomalyStatus status = state.getStatus(2); Date timestamp = timeFormat.parse(timeStr); boolean isZero = Math.abs(currentValue) < 0.001; if (isZero) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(2, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(2, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("零值检测错误: " + config.encode + " - " + e.getMessage()); } } // 阈值检测 private void checkThresholdAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { try { if (config.ishigh == 1) { AnomalyStatus highStatus = state.getStatus(3); processThresholdAnomaly(highStatus, currentValue, timeStr, currentValue > config.highthreshold, config, 3, out); } if (config.islow == 1) { AnomalyStatus lowStatus = state.getStatus(4); processThresholdAnomaly(lowStatus, currentValue, timeStr, currentValue < config.lowthreshold, config, 4, out); } } catch (Exception e) { System.err.println("阈值检测错误: " + config.encode + " - " + e.getMessage()); } } private void processThresholdAnomaly(AnomalyStatus status, double currentValue, String timeStr, boolean isAnomaly, ULEParamConfig config, int anomalyType, Collector<JSONObject> out) { try { Date timestamp = timeFormat.parse(timeStr); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(anomalyType, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(anomalyType, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("阈值处理错误: " + config.encode + " - " + e.getMessage()); } } // 同步检测 private void checkSyncAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, ConfigCollection configCollection, Collector<JSONObject> out) { if (config.issync != 1 || config.syncparaencode == null) return; try { AnomalyStatus status = state.getStatus(6); Date timestamp = timeFormat.parse(timeStr); ULEParamConfig relatedConfig = configCollection.encodeToConfig.get(config.syncparaencode); if (relatedConfig == null) return; String relatedTag = null; for (Map.Entry<String, List<ULEParamConfig>> entry : configCollection.tagToConfigs.entrySet()) { if (entry.getValue().contains(relatedConfig)) { relatedTag = entry.getKey(); break; } } if (relatedTag == null) return; Double relatedValue = lastValuesMap.get(relatedTag); if (relatedValue == null) return; boolean isAnomaly = (Math.abs(currentValue - 1.0) < 0.001) && (Math.abs(relatedValue) < 0.001); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(6, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(6, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("同步检测错误: " + config.encode + " - " + e.getMessage()); } } // 报告异常 private void reportAnomaly(int anomalyType, int statusFlag, double value, String time, ULEParamConfig config, Collector<JSONObject> out) { JSONObject event = new JSONObject(); event.put("tag", config.tag); event.put("paracode", config.encode); event.put("abnormaltype", anomalyType); event.put("statusflag", statusFlag); event.put("datavalue", value); event.put("triggertime", time); out.collect(event); } @Override public void processBroadcastElement(ConfigCollection newConfig, Context ctx, Collector<JSONObject> out) { BroadcastState<Void, ConfigCollection> state = ctx.getBroadcastState(Descriptors.configStateDescriptor); try { state.put(null, newConfig); // 仅更新广播状态 } catch (Exception e) { e.printStackTrace(); } System.out.println("广播配置更新完成, 配置项: " + newConfig.configCount()); } // ========== 辅助方法 ========== private ConfigCollection getBroadcastConfig(ReadOnlyContext ctx) throws Exception { return ctx.getBroadcastState(Descriptors.configStateDescriptor).get(null); } private AnomalyState getOrCreateState(String encode) throws Exception { AnomalyState state = stateMap.get(encode); if (state == null) { state = new AnomalyState(); } return state; } } // 异常状态类 public static class AnomalyState implements Serializable { private static final long serialVersionUID = 1L; private final Map<Integer, AnomalyStatus> statusMap = new HashMap<>(); public AnomalyStatus getStatus(int type) { return statusMap.computeIfAbsent(type, k -> new AnomalyStatus()); } } // 异常状态详情 public static class AnomalyStatus implements Serializable { private static final long serialVersionUID = 1L; public Date startTime; // 异常开始时间 public Double lastValue; // 用于恒值检测 public Date lastChangeTime; // 值最后变化时间 public boolean reported; // 是否已报告 public void reset() { startTime = null; lastValue = null; lastChangeTime = null; reported = false; } } }上述代码需完善内容,1、离线监测未实现,当配置中有条的tag更改,且流数据中没有该tag,未报出离线异常;2、当参数调整时,已有的报警状态参数,配置成不启用相应的报警如何处理;3、当参数配置删除时,已有的报警状态参数处理。,请完善上述内容,生成完整优化后可运行代码。
08-01
import rclpy from rclpy.node import Node from sensor_msgs.msg import CompressedImage from geometry_msgs.msg import Twist from cv_bridge import CvBridge import cv2 import numpy as np import time import math from test1_yolo_bin import YoloNV12Inferencer class LineAndObstacleNode(Node): def __init__(self): super().__init__('line_and_obstacle_node') self.bridge = CvBridge() self.subscription = self.create_subscription( CompressedImage, '/image', self.image_callback, 10) self.publisher_ = self.create_publisher(Twist, '/cmd_vel', 10) # YOLO 模型加载 try: self.inferencer = YoloNV12Inferencer('/root/dev_ws/3w/bin/yolov5s_672x672_nv12.bin') self.get_logger().info("✅ YOLO模型加载成功") except Exception as e: self.get_logger().error(f"YOLO加载失败: {e}") self.inferencer = None # 避障参数 self.focal_length = 550.0 self.image_cx = 320.0 self.target_height = 0.3 self.repulsion_range = 0.5 self.state = 'normal' # normal / avoiding_turn / avoiding_straight / realignment self.avoid_start_time = 0 self.obstacle_list = [] # 新增:航向角记录 self.target_yaw = 0.0 # 避障前原始航向 self.current_yaw = 0.0 # 当前航向(简化的航向估计) self.last_yaw_update = time.time() # PID控制参数(回正阶段使用) self.Kp = 1.5 # 比例系数 self.Ki = 0.1 # 积分系数 self.Kd = 0.3 # 微分系数 self.prev_error = 0.0 self.integral = 0.0 # 车道线检测参数 self.min_speed = 0.25 self.lost_lane_counter = 0 self.lost_lane_threshold = 5 self.current_frame = None self.last_valid_alpha = 0.0 # 状态超时参数 self.REALIGN_TIMEOUT = 3.0 # 回正阶段最大超时时间 self.AVOID_TURN_DURATION = 0.6 # 避障转向持续时间 self.YAW_THRESHOLD = 0.1 # 航向角误差阈值(弧度) self.timer = self.create_timer(0.1, self.control_loop) def image_callback(self, msg): try: img = self.bridge.compressed_imgmsg_to_cv2(msg, desired_encoding='bgr8') # 保存原始图像用于YOLO检测 self.raw_image = img.copy() # --- 巡线区域处理 --- h1, width = img.shape[:2] crop = img[int(h1 * 0.4):, :] self.current_frame = cv2.resize(crop, (160, 120)) # --- 简化的航向估计(基于最后有效转向角)--- # 在实际应用中应使用IMU数据 current_time = time.time() time_diff = current_time - self.last_yaw_update if time_diff > 0: # 角速度 = 转向角 / 时间 angular_velocity = self.last_valid_alpha / time_diff self.current_yaw += angular_velocity * time_diff # 角度归一化 [-π, π] self.current_yaw = self.normalize_angle(self.current_yaw) self.last_yaw_update = current_time # --- YOLO处理 --- if self.inferencer: detections = self.inferencer.infer(self.raw_image) self.obstacle_list = [] for class_id, score, (x1, y1, x2, y2) in detections: h = y2 - y1 if h <= 0: continue distance = (self.target_height * self.focal_length) / h cx = (x1 + x2) / 2 offset_y = (cx - self.image_cx) * distance / self.focal_length if class_id == 0: # 假设0是障碍物类别 self.obstacle_list.append((distance, offset_y)) except Exception as e: self.get_logger().error(f"图像处理错误: {e}") def normalize_angle(self, angle): """将角度归一化到[-π, π]范围""" while angle > math.pi: angle -= 2 * math.pi while angle < -math.pi: angle += 2 * math.pi return angle def pid_control(self, target, current): """PID控制器计算角速度""" error = self.normalize_angle(target - current) # PID计算 self.integral += error * 0.1 # 0.1是控制周期 derivative = (error - self.prev_error) / 0.1 # 抗积分饱和 self.integral = max(min(self.integral, 1.0), -1.0) # 计算输出 output = self.Kp * error + self.Ki * self.integral + self.Kd * derivative self.prev_error = error # 输出限制 return max(min(output, 1.0), -1.0) def detect_lane_line(self, frame): """检测车道线并返回速度、转向角和检测状态""" if frame is None: return 0.0, 0.0, False try: hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 黑色线条 HSV 阈值 lower_black = np.array([0, 0, 0]) upper_black = np.array([180, 255, 50]) black_mask = cv2.inRange(hsv, lower_black, upper_black) # 形态学操作增强检测 kernel = np.ones((5, 5), np.uint8) black_mask = cv2.morphologyEx(black_mask, cv2.MORPH_OPEN, kernel) black_mask = cv2.dilate(black_mask, kernel, iterations=2) # 检测区域高度 detection_band = 40 x_coords = np.where(black_mask[-detection_band:] == 255)[1] if len(x_coords) > 50: # 确保有足够的像素点 self.lost_lane_counter = 0 mean_x = np.mean(x_coords) h, w = frame.shape[:2] alpha = float(-(mean_x - w / 2) / (w / 2)) self.last_valid_alpha = alpha # 保存有效转向角 return 0.25, alpha, True else: self.lost_lane_counter += 1 if self.lost_lane_counter >= self.lost_lane_threshold: return 0.0, 0.0, False else: # 使用最后有效的转向角 return 0.10, self.last_valid_alpha, False except Exception as e: self.get_logger().error(f"车道线检测错误: {e}") return 0.0, 0.0, False def control_loop(self): now = time.time() elapsed_time = now - self.avoid_start_time # 障碍物检测(仅考虑前方0.5米内的障碍物) valid_obs = [o for o in self.obstacle_list if 0 < o[0] < self.repulsion_range and abs(o[1]) < 0.5] # 状态机实现 if self.state == 'avoiding_turn': # 右转避障阶段 if elapsed_time < self.AVOID_TURN_DURATION: self.publish_cmd(0.15, -0.8) # 右转避障 else: # 记录避障前的原始航向作为回正目标 self.target_yaw = self.current_yaw self.get_logger().info(f"📐 记录目标航向: {self.target_yaw:.2f} rad") self.state = 'realignment' self.avoid_start_time = now self.prev_error = 0.0 # 重置PID误差 self.integral = 0.0 # 重置积分项 self.get_logger().info("🔄 进入回正阶段") elif self.state == 'realignment': # 回正阶段(使用PID控制精准回正) angular_vel = self.pid_control(self.target_yaw, self.current_yaw) # 检查是否完成回正 error = abs(self.normalize_angle(self.target_yaw - self.current_yaw)) if error < self.YAW_THRESHOLD: self.state = 'normal' self.get_logger().info(f"✅ 回正完成, 误差: {error:.3f} rad") # 回正后尝试检测车道线 v, alpha, lane_detected = self.detect_lane_line(self.current_frame) if lane_detected: self.publish_cmd(v, alpha) else: self.publish_cmd(0.1, 0.0) # 低速直行寻找车道线 elif elapsed_time > self.REALIGN_TIMEOUT: self.state = 'normal' self.get_logger().warn("⏱️ 回正超时,强制恢复巡线") self.publish_cmd(0.1, 0.0) # 低速直行寻找车道线 else: # 使用PID控制进行回正 self.publish_cmd(0.1, angular_vel) self.get_logger().info(f"⏳ 回正中: 目标={self.target_yaw:.2f}, 当前={self.current_yaw:.2f}, 误差={error:.3f}") elif valid_obs and self.state == 'normal': # 检测到障碍物,开始避障 self.state = 'avoiding_turn' self.avoid_start_time = now self.get_logger().warn(f"🚧 检测到障碍物,距离={min(o[0] for o in valid_obs):.2f}m") else: # 正常巡线模式 v, alpha, lane_detected = self.detect_lane_line(self.current_frame) if lane_detected: self.publish_cmd(v, alpha) else: # 未检测到车道线时的安全策略 self.publish_cmd(0.05, self.last_valid_alpha) def publish_cmd(self, speed, steer): """发布控制指令""" msg = Twist() msg.linear.x = float(speed) msg.angular.z = float(steer) self.publisher_.publish(msg) def main(args=None): rclpy.init(args=args) node = LineAndObstacleNode() try: rclpy.spin(node) except KeyboardInterrupt: pass finally: node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() 按照这个来增加修改
07-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值