| | |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import javax.xml.XMLConstants; |
| | | import javax.xml.parsers.DocumentBuilder; |
| | | import javax.xml.parsers.DocumentBuilderFactory; |
| | | import org.w3c.dom.Document; |
| | | import org.w3c.dom.Element; |
| | |
| | | import org.w3c.dom.Node; |
| | | import org.w3c.dom.NodeList; |
| | | import org.xml.sax.InputSource; |
| | | import org.xml.sax.helpers.DefaultHandler; |
| | | import com.ruoyi.device.model.ChannelIdentity; |
| | | import com.ruoyi.device.model.DetectedChannel; |
| | | import com.ruoyi.device.model.DeviceWorkStatus; |
| | | import com.ruoyi.device.model.HistoryAvailability; |
| | | import com.ruoyi.device.model.HistoryPage; |
| | | import com.ruoyi.device.model.HistorySample; |
| | | import com.ruoyi.device.model.Measurement; |
| | |
| | | return envelope(list.append("</list>").toString()); |
| | | } |
| | | |
| | | /** |
| | | * 查询各通道当前 BTS 测试的历史数据上传进度。 |
| | | */ |
| | | public String inquireHistoryRequest(Set<ChannelIdentity> channels) |
| | | { |
| | | StringBuilder list = new StringBuilder("<cmd>inquiredf</cmd><list count=\"").append(channels.size()).append("\">"); |
| | | for (ChannelIdentity channel : channels) |
| | | { |
| | | requireOwned(channel); |
| | | list.append("<chl devtype=\"").append(channel.devtype()) |
| | | .append("\" devid=\"").append(channel.devid()) |
| | | .append("\" subdevid=\"").append(channel.subdevid()) |
| | | .append("\" chlid=\"").append(channel.chlid()) |
| | | .append("\" testid=\"0\"/>"); |
| | | } |
| | | return envelope(list.append("</list>").toString()); |
| | | } |
| | | |
| | | public String downloadRequest(ChannelIdentity channel, String btsTestId, long startPosition, int limit) |
| | | { |
| | | requireOwned(channel); |
| | |
| | | ChannelIdentity identity = new ChannelIdentity(adapterCode, integerAttribute(element, "devtype"), |
| | | integerAttribute(element, "devid"), integerAttribute(element, "subdevid"), chlid); |
| | | channels.add(new DetectedChannel(identity, requiredAttribute(element, "ip"), |
| | | Boolean.parseBoolean(element.getTextContent().trim()))); |
| | | booleanText(element.getTextContent(), "available"))); |
| | | } |
| | | return List.copyOf(channels); |
| | | } |
| | |
| | | { |
| | | Element element = (Element) nodes.item(i); |
| | | String[] device = requiredAttribute(element, "dev").split("-"); |
| | | if (device.length < 4) throw new BtsProtocolException("Invalid BTS dev identity: " + element.getAttribute("dev")); |
| | | if (device.length < 4) |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE, |
| | | "Invalid BTS dev identity: " + element.getAttribute("dev")); |
| | | ChannelIdentity identity = new ChannelIdentity(adapterCode, integer(device[0], "devtype"), integer(device[1], "devid"), |
| | | integer(device[2], "subdevid"), integer(device[3], "chlid")); |
| | | snapshots.put(identity, new RealtimeSnapshot(identity, |
| | |
| | | return Map.copyOf(snapshots); |
| | | } |
| | | |
| | | public HistoryPage parseHistory(String xml, ChannelIdentity channel, long requestedStart) |
| | | /** |
| | | * 将 BTS 历史清单映射为厂商无关的通道历史可用性。 |
| | | */ |
| | | public List<HistoryAvailability> parseHistoryAvailability(String xml) |
| | | { |
| | | Document document = parse(xml, "inquiredf_resp"); |
| | | NodeList nodes = document.getElementsByTagName("chl"); |
| | | List<HistoryAvailability> availability = new ArrayList<>(nodes.getLength()); |
| | | for (int i = 0; i < nodes.getLength(); i++) |
| | | { |
| | | Element element = (Element) nodes.item(i); |
| | | ChannelIdentity channel = new ChannelIdentity(adapterCode, integerAttribute(element, "devtype"), |
| | | integerAttribute(element, "devid"), integerAttribute(element, "subdevid"), |
| | | integerAttribute(element, "chlid")); |
| | | availability.add(new HistoryAvailability(channel, requiredAttribute(element, "testid"), |
| | | longAttribute(element, "count"), booleanText(element.getTextContent(), "uploadComplete"))); |
| | | } |
| | | return List.copyOf(availability); |
| | | } |
| | | |
| | | public HistoryPage parseHistory(String xml, ChannelIdentity channel, String requestedTestId, long requestedStart) |
| | | { |
| | | Document document = parse(xml, "download_resp"); |
| | | Element download = firstElement(document, "download"); |
| | | String btsTestId = requiredAttribute(download, "testid"); |
| | | if (integerAttribute(download, "devtype") != channel.devtype() |
| | | || integerAttribute(download, "devid") != channel.devid() |
| | | || integerAttribute(download, "subdevid") != channel.subdevid() |
| | | || integerAttribute(download, "chlid") != channel.chlid() |
| | | || !btsTestId.equals(requestedTestId)) |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.IDENTITY_MISMATCH, |
| | | "BTS history response identity does not match the request"); |
| | | NodeList nodes = document.getElementsByTagName("data"); |
| | | List<HistorySample> samples = new ArrayList<>(nodes.getLength()); |
| | | long next = requestedStart; |
| | |
| | | factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); |
| | | factory.setXIncludeAware(false); |
| | | factory.setExpandEntityReferences(false); |
| | | Document document = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml))); |
| | | DocumentBuilder builder = factory.newDocumentBuilder(); |
| | | builder.setErrorHandler(new DefaultHandler()); |
| | | Document document = builder.parse(new InputSource(new StringReader(xml))); |
| | | String command = firstElement(document, "cmd").getTextContent().trim(); |
| | | if (!expectedCommand.equals(command)) |
| | | throw new BtsProtocolException("Expected BTS command " + expectedCommand + " but received " + command); |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.UNEXPECTED_COMMAND, |
| | | "Expected BTS command " + expectedCommand + " but received " + command); |
| | | return document; |
| | | } |
| | | catch (BtsProtocolException ex) |
| | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | throw new BtsProtocolException("Invalid BTS XML response", ex); |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.MALFORMED_XML, |
| | | "Invalid BTS XML response", ex); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | catch (DateTimeParseException ex) |
| | | { |
| | | throw new BtsProtocolException("Invalid BTS device time: " + value, ex); |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE, |
| | | "Invalid BTS device time: " + value, ex); |
| | | } |
| | | } |
| | | |
| | |
| | | private static Element firstElement(Document document, String tag) |
| | | { |
| | | NodeList elements = document.getElementsByTagName(tag); |
| | | if (elements.getLength() == 0) throw new BtsProtocolException("Missing BTS element: " + tag); |
| | | if (elements.getLength() == 0) |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.MISSING_FIELD, "Missing BTS element: " + tag); |
| | | return (Element) elements.item(0); |
| | | } |
| | | |
| | | private static String requiredAttribute(Element element, String name) |
| | | { |
| | | String value = element.getAttribute(name); |
| | | if (value == null || value.isBlank()) throw new BtsProtocolException("Missing BTS attribute: " + name); |
| | | if (value == null || value.isBlank()) |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.MISSING_FIELD, "Missing BTS attribute: " + name); |
| | | return value.trim(); |
| | | } |
| | | |
| | |
| | | } |
| | | catch (NumberFormatException ex) |
| | | { |
| | | throw new BtsProtocolException("Invalid integer BTS attribute: " + name, ex); |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE, |
| | | "Invalid integer BTS attribute: " + name, ex); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | catch (NumberFormatException ex) |
| | | { |
| | | throw new BtsProtocolException("Invalid integer BTS attribute: " + name, ex); |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE, |
| | | "Invalid integer BTS attribute: " + name, ex); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | catch (NumberFormatException ex) |
| | | { |
| | | throw new BtsProtocolException("Invalid integer BTS attribute: " + name, ex); |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE, |
| | | "Invalid integer BTS attribute: " + name, ex); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | catch (NumberFormatException ex) |
| | | { |
| | | throw new BtsProtocolException("Invalid decimal BTS attribute: " + name, ex); |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE, |
| | | "Invalid decimal BTS attribute: " + name, ex); |
| | | } |
| | | } |
| | | |
| | | private static boolean booleanText(String value, String name) |
| | | { |
| | | String normalized = value == null ? "" : value.trim(); |
| | | if ("true".equalsIgnoreCase(normalized)) return true; |
| | | if ("false".equalsIgnoreCase(normalized)) return false; |
| | | throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE, |
| | | "Invalid boolean BTS value: " + name); |
| | | } |
| | | } |